BUG/GetVisual3DTotalTransform
#1
I'd like to report a bug with this method:

public static Transform3D GetVisual3DTotalTransform(Visual3D rootVisual3D, Visual3D finalVisual3D, bool addFinalVisualTransformation, out bool isVisual3DConnected)

Specifically this line: transformGroup.Children.Insert(0, oneTransform);

The bug: The method returns the wrong order in the matrix multiplication. It returns T_Parent, T_Child instead of T_Child, T_Parent. I provide sample unit test in text file attachment where the first assert passes (group vs SRT) and the second assert fails (group from the method vs SRT).

Expected behavior: Given this hierarchy of objects
Visual3D - name: "Translation"
   Visual3D - name: "Rotation"
      Visual3D - name: "Scale"
I expect to get transformation matrix that works like this: T * R * S * point.

Usage: 
var mR = new ContentVisual3D();
var mS = new ContentVisual3D();
var mT = new ContentVisual3D();
mR.SetName("Rotation");
mS.SetName("Scale");
mT.SetName("Translation");
mS.Transform = S;
mR.Transform = R;
mT.Transform = T;

mR.Children.Add(mS);
mT.Children.Add(mR);

TransformationsHelper.GetVisual3DTotalTransform(mT, mS, true, out Transform3D visualSRT);

What I get: 
Given this insertion into the transformation group transformGroup.Children.Insert(0, oneTransform) the transformation I get is S * R * T * point, which is not correct order of transformations. It could be worked around, but when the hieararchy is not simply these basic transformations it is not possible to change the order (especially since it is bound to the rendering).

If not feature: Simply replacing Insert with Add should solve this.

Best regards,
Janovský Roman


Attached Files
.txt   testmethod.txt (Size: 1.3 KB / Downloads: 1)
#2
I can confirm that this is actually a bug.

You are right, the Insert method produces a group of transformations in an invalid order.

I have checked the usage of this method and it is not used anywhere in Ab3d.PowerToys library. But it is used in the LinesSelector sample. In case the 3D lines are defined in a hierarchy of Visual3D objects with more that one Transformation set, then the code from this sample will not work correctly because of this bug.

The fixed code will be available in the next version. 
Until the release you can use the following code (the fixed version of the method):

Code:
       /// <summary>
       /// GetVisual3DTotalTransform returns a Transform3D that contains all the transformations from the Viewport3D to the finalVisual3D.
       /// If no transformation is found, then null is returned.
       /// The isVisual3DConnected out parameter is set to false, when the Viewport3D is not reached from the finalVisual3D.
       /// </summary>
       /// <remarks>
       /// <para>
       /// <b>GetVisual3DTotalTransform</b> returns a Transform3D that contains all the transformations from the Viewport3D to the finalVisual3D.
       /// </para>
       /// <para>
       /// If no transformation is found, then null is returned.
       /// </para>
       /// <para>
       /// The isVisual3DConnected out parameter is set to false, when the Viewport3D is not reached from the finalVisual3D.
       /// </para>
       /// <para>
       /// GetVisual3DTotalTransform can be used when we want to get the transformation of a Visual3D that is inside a hierarchy of other Visual3D objects.
       /// </para>
       /// <para>
       /// This method is the same as <see cref="GetVisual3DTotalTransform(Visual3D, Visual3D, bool, out bool)"/> is called with first parameter (rootVisual3D) set to null.
       /// </para>
       /// </remarks>
       /// <param name="finalVisual3D">The Visual3D object for witch the Transform3D is created</param>
       /// <param name="addFinalVisualTransformation">if true then the returned Transform3D also contains the transform of the finalVisual3D</param>
       /// <param name="isVisual3DConnected">out Boolean parameter that is set to true, when finalVisual3D is connected to the rootVisual3D or Viewport3D; false when finalVisual3D is not reached from the finalVisual3D or Viewport3D.</param>
       /// <returns>Transform3D or null if no transformation is found</returns>
       public static Transform3D GetVisual3DTotalTransform(Visual3D finalVisual3D, bool addFinalVisualTransformation, out bool isVisual3DConnected)
       {
           return GetVisual3DTotalTransform(null, finalVisual3D, addFinalVisualTransformation, out isVisual3DConnected);
       }

       /// <summary>
       /// GetVisual3DTotalTransform returns true when the specified finalVisual3D can be reached from the Viewport3D.
       /// In this case the totalTransform3D out parameter is set to the a Transform3D that contains all the transformations from the Viewport3D to the finalVisual3D.
       /// If no transformation is found, then totalTransform3D is set to null.
       /// </summary>
       /// <remarks>
       /// <para>
       /// <b>GetVisual3DTotalTransform</b> returns true when the specified finalVisual3D can be reached from the Viewport3D.
       /// In this case the totalTransform3D out parameter is set to the a Transform3D that contains all the transformations from the Viewport3D to the finalVisual3D.
       /// </para>
       /// <para>
       /// If no transformation is found, then totalTransform3D is set to null.
       /// </para>
       /// <para>
       /// GetVisual3DTotalTransform can be used when we want to get the transformation of a Visual3D that is inside a hierarchy of other Visual3D objects.
       /// </para>
       /// <para>
       /// This method is the same as <see cref="GetVisual3DTotalTransform(Visual3D, Visual3D, bool, out Transform3D)"/> is called with first parameter (rootVisual3D) set to null.
       /// </para>
       /// </remarks>
       /// <param name="finalVisual3D">The Visual3D object for witch the Transform3D is created</param>
       /// <param name="addFinalVisualTransformation">if true then the returned Transform3D also contains the transform of the finalVisual3D</param>
       /// <param name="totalTransform3D">Transform3D or null if no transformation is found</param>
       /// <returns>returns true, when finalVisual3D is connected to the rootVisual3D or Viewport3D; false when finalVisual3D is not reached from the finalVisual3D or Viewport3D.</returns>
       public static bool GetVisual3DTotalTransform(Visual3D finalVisual3D, bool addFinalVisualTransformation, out Transform3D totalTransform3D)
       {
           bool isVisual3DConnected;
           totalTransform3D = GetVisual3DTotalTransform(null, finalVisual3D, addFinalVisualTransformation, out isVisual3DConnected);

           return isVisual3DConnected;
       }

       /// <summary>
       /// GetVisual3DTotalTransform returns true when the specified finalVisual3D can be reached from the rootVisual3D.
       /// In this case the totalTransform3D out parameter is set to the a Transform3D that contains all the transformations from the rootVisual3D to the finalVisual3D.
       /// If no transformation is found, then totalTransform3D is set to null.
       /// </summary>
       /// <remarks>
       /// <para>
       /// <b>GetVisual3DTotalTransform</b> returns true when the specified finalVisual3D can be reached from the rootVisual3D.
       /// In this case the totalTransform3D out parameter is set to the a Transform3D that contains all the transformations from the rootVisual3D to the finalVisual3D.
       /// </para>
       /// <para>
       /// If no transformation is found, then totalTransform3D is set to null.
       /// </para>
       /// <para>
       /// GetVisual3DTotalTransform can be used when we want to get the transformation of a Visual3D that is inside a hierarchy of other Visual3D objects.
       /// </para>
       /// </remarks>
       /// <param name="rootVisual3D">The Visual3D object that is the root of the hierarchy where the finalVisual3D is (if null then Viewport3D is considered the root object).</param>
       /// <param name="finalVisual3D">The Visual3D object for witch the Transform3D is created</param>
       /// <param name="addFinalVisualTransformation">if true then the returned Transform3D also contains the transform of the finalVisual3D</param>
       /// <param name="totalTransform3D">Transform3D or null if no transformation is found</param>
       /// <returns>returns true, when finalVisual3D is connected to the rootVisual3D or Viewport3D; false when finalVisual3D is not reached from the finalVisual3D or Viewport3D.</returns>
       public static bool GetVisual3DTotalTransform(Visual3D rootVisual3D, Visual3D finalVisual3D, bool addFinalVisualTransformation, out Transform3D totalTransform3D)
       {
           bool isVisual3DConnected;
           totalTransform3D = GetVisual3DTotalTransform(rootVisual3D, finalVisual3D, addFinalVisualTransformation, out isVisual3DConnected);

           return isVisual3DConnected;
       }

       /// <summary>
       /// GetVisual3DTotalTransform returns a Transform3D that contains all the transformations from the rootVisual3D to the finalVisual3D.
       /// When rootVisual3D is null, then transformation from Viewport3D to the finalVisual3D is returned.
       /// If no transformation is found, then null is returned.
       /// The isVisual3DConnected out parameter is set to false, when the rootVisual3D or Viewport3D is not reached from the finalVisual3D.
       /// </summary>
       /// <remarks>
       /// <para>
       /// <b>GetModelTotalTransform</b> returns a Transform3D that contains all the transformations from the rootVisual3D to the finalVisual3D.
       /// When rootVisual3D is null, then transformation from Viewport3D to the finalVisual3D is returned.
       /// </para>
       /// <para>
       /// If no transformation is found, then null is returned.
       /// </para>
       /// <para>
       /// The isVisual3DConnected out parameter is set to when finalVisual3D is connected to the rootVisual3D or Viewport3D; false when finalVisual3D is not reached from the finalVisual3D or Viewport3D.
       /// </para>
       /// <para>
       /// GetVisual3DTotalTransform can be used when we want to get the transformation of a Visual3D that is inside a hierarchy of other Visual3D objects.
       /// </para>
       /// </remarks>
       /// <param name="rootVisual3D">The Visual3D object that is the root of the hierarchy where the finalVisual3D is (if null then Viewport3D is considered the root object).</param>
       /// <param name="finalVisual3D">The Visual3D object for witch the Transform3D is created</param>
       /// <param name="addFinalVisualTransformation">if true then the returned Transform3D also contains the transform of the finalVisual3D</param>
       /// <param name="isVisual3DConnected">out Boolean parameter that is set to true, when finalVisual3D is connected to the rootVisual3D or Viewport3D; false when finalVisual3D is not reached from the finalVisual3D or Viewport3D.</param>
       /// <returns>Transform3D or null if no transformation is found</returns>
       public static Transform3D GetVisual3DTotalTransform(Visual3D rootVisual3D, Visual3D finalVisual3D, bool addFinalVisualTransformation, out bool isVisual3DConnected)
       {
           if (finalVisual3D == null)
               throw new ArgumentNullException("finalVisual3D", "finalVisual3D is null.");

           if (ReferenceEquals(rootVisual3D, finalVisual3D))
           {
               isVisual3DConnected = true;
               return null;
           }


           Visual3D oneVisual3D = finalVisual3D;

           var  transformGroup        = new Transform3DGroup();
           bool addNextTransformation = addFinalVisualTransformation; // We add the first transformation (finalVisual3D.Transform) only when addFinalModelTransformation is true

           // First append all parent Visual3D transformations
           while (oneVisual3D != null)
           {
               if (addNextTransformation)
               {
                   var oneTransform = oneVisual3D.Transform;

                   if (oneTransform != null && !oneTransform.Value.IsIdentity)
                       transformGroup.Children.Add(oneTransform);
               }
               else
               {
                   addNextTransformation = true; // We skip first transformation when addFinalModelTransformation is false but add all following transformations.
               }

               if (rootVisual3D != null && ReferenceEquals(rootVisual3D, oneVisual3D)) // If rootVisual3D is not null, we stop when rootVisual3D is reached
                   break;


               var oneParent = VisualTreeHelper.GetParent(oneVisual3D);


               // If rootVisual3D is null, we stop when we reach Viewport3D
               // If rootVisual3D is not null, we will stop when finalVisual3D is reached - this is done after the transformations are applied
               if (rootVisual3D == null && oneParent is Viewport3DVisual)
                   break;

               oneVisual3D = oneParent as Visual3D;
           }


           isVisual3DConnected = (oneVisual3D != null);

           if (transformGroup.Children.Count == 0)
               return null;

           if (transformGroup.Children.Count == 1)
               return transformGroup.Children[0];

           return transformGroup;
       }
Andrej Benedik
  


Forum Jump:


Users browsing this thread:
1 Guest(s)