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;
}