Transform

In some upcoming posts, I would like to take a look at the how to handle transformations of Revit elements, as well as locations of nested linked files.
Before we get into that, let us take a closer look at the underlying Revit API Transform class.
We briefly mentioned it in the discussion of the Revit API

geometry library
,
and demonstrated some basic use of it for

transforming a polygon

from 3D to 2D.

The Revit API Transform class is defined in the Autodesk.Revit.Geometry namespace and represents a transformation of the affine 3-space.
It provides the typical properties and methods expected for a transformation class:

  • Multiply, multiply two transforms.
  • ScaleBasis, scale the basis vectors.
  • ScaleBasisAndOrigin, scale the basis vectors and the origin.
  • Basis, BasisX, BasisY, BasisZ, return the base axes.
  • Determinant, return the determinant.
  • HasReflection, to tell whether this transformation is a reflection.
  • Identity, the identity transformation.
  • Inverse, the inverse transformation.
  • IsConformal, to tell whether this transformation is conformal.
  • IsIdentity, to tell whether this transformation is the identity.
  • IsTranslation, to tell whether this transformation is a translation.
  • Origin, the origin.
  • Reflection, returns a transformation that reflects about a specified plane.
  • Rotation, returns a transformation that rotates by a specified angle about a specified axis and point.
  • Scale, return the scale of the transformation.
  • Translation, returns a transformation that translates by a specified vector.

In some other APIs, transformations are handled by matrices.
The Revit API generally tries to address a higher level of abstraction, so the class is defined as a generic transformation class, and the underlying implementation is not explicitly exposed by the API.

The Creation.Application does not provide any methods to create transform objects.
A Transform instance can be created using the new operator as a copy constructor, or by using some of the static methods of the Transform class.
The transform default constructor initializes it to identity:


Transform trans1, trans2, trans3;

Here are examples of using the static Transform class methods to define rotation, translation, scaling and mirroring transforms:


XYZ ptOrigin = XYZ.Zero;
XYZ ptXAxis = XYZ.BasisX;
XYZ ptYAxis = XYZ.BasisY;
 
trans1 = Transform.get_Rotation( // rotation
  ptOrigin, ptXAxis, 90 );
 
trans2 = Transform.get_Translation( // translation
  ptXAxis );
 
trans1 = trans2.ScaleBasis( 2.0 ); // scaling
 
Plane plane1 = creApp.NewPlane(
  ptXAxis, ptYAxis );
 
trans3 = Transform.get_Reflection( // mirror
  plane1 );

One simple use of a transform is to translate a point:


p3 = trans1.OfPoint( p2 );

Some further examples are given by the Revit API
tips and tricks sample code,
in the region ‘1. XYZ and Transform’ in RevitGeometry.cs.
Besides these transforms, they also show how to invert and concatenate transformations.

The polygon transformation is implemented by code in the region ‘Transform 3D plane to horizontal’ in The Building Coder sample code external command class

CmdWallProfileArea
,
specifically in the methods GetTransformToZ and ApplyTransform:


Transform GetTransformToZ( XYZ v )
{
  Transform t;
 
  double a = XYZ.BasisZ.Angle( v );
 
  if( Util.IsZero( a ) )
  {
    t = Transform.Identity;
  }
  else
  {
    XYZ axis = Util.IsEqual( a, Math.PI )
      ? XYZ.BasisX
      : v.Cross( XYZ.BasisZ );
 
    t = Transform.get_Rotation( XYZ.Zero,
      axis, a );
  }
  return t;
}
 
List<XYZ> ApplyTransform(
  List<XYZ> polygon,
  Transform t )
{
  int n = polygon.Count;
 
  List<XYZ> polygonTransformed
    = new List<XYZ>( n );
 
  foreach( XYZ p in polygon )
  {
    polygonTransformed.Add( t.OfPoint( p ) );
  }
  return polygonTransformed;
}

GetTransformToZ calculates the rotation required to transform a planar polygon from its arbitrary orientation in 3D space to a plane parallel to the XY plane.
The algorithm used is described in detail in the discussion on

transforming a polygon
.
ApplyTransform applies the rotation to the 3D polygon to obtain a rotated copy which is parallel to the XY plane.
It does so by iterating through the points of the original polygon and calculating the rotated point using Transform.OfPoint on each.


Comments

3 responses to “Transform”

  1. Do you know how to get a 4×4 transformation matrix from a Revit Transform object? Thanks.

  2. Dear Bruno,
    Thank you for the question, which I wish I had thought of covering when creating the original post.
    To represent an affine transformation in 3D space, you normally only use a 3 by 4 portion of the coordinates of a 4 by 4 transformation matrix. The last row is just added to make it square, and contains the coordinates 0,0,0,1. The first three rows are the X, Y and Z coordinates of three basis vectors and the origin of the transformation.
    The Util.TransformString method in The Building Coder sample code shows how to extract and print these 3 x 4 or 12 coordinate values:
    static public string RealString( double a )
    {
    return a.ToString( “0.##” );
    }
    static public string PointString( XYZ p )
    {
    return string.Format( “({0},{1},{2})”,
    RealString( p.X ), RealString( p.Y ),
    RealString( p.Z ) );
    }
    static public string TransformString( Transform t )
    {
    return string.Format( “({0},{1},{2},{3})”, PointString( t.Origin ),
    PointString( t.BasisX ), PointString( t.BasisY ), PointString( t.BasisZ ) );
    }
    How you put this data together to define your 4 by 4 matrix depends on the exact definition and usage of the matrix you are populating.
    As always, Wikipedia provides some useful background information:
    http://en.wikipedia.org/wiki/Transformation_matrix
    Cheers, Jeremy.

  3. I have a problem. When I use RevitAPI , I found that the Tranform method is not accurate.The code below:
    public void transform()
    {
    XYZ locate = new XYZ(0, 0, 0);
    XYZ Vec = new XYZ(1, 0, 1);
    Transform trans,trans1;
    trans = Transform.get_Rotation(locate, Vec, Math.PI / 2.0 * Math.PI / 180);
    trans1 = Transform.get_Rotation(locate, Vec, Math.PI / 2.0 * Math.PI / 180);
    XYZ old_pt = new XYZ(0, 0, 0);
    XYZ old_Vec = new XYZ(1, 0, 1);
    XYZ old_Vec2 = new XYZ(1, 0, -1);
    XYZ temp_pt = trans.OfPoint(old_pt);
    XYZ temp_Vec = trans.OfVector(old_Vec);
    XYZ temp_Vec2 = trans.OfVector(old_Vec2);
    XYZ new_pt = trans.OfPoint(temp_pt);
    XYZ new_Vec = trans.OfVector(temp_Vec);
    XYZ new_Vec2 = trans.OfVector(temp_Vec2);
    MessageBox.Show(temp_Vec2.X.ToString() + “,” + temp_Vec2.Y.ToString() + “,” + temp_Vec2.Z.ToString());
    MessageBox.Show(new_Vec2.X.ToString() + “,” + new_Vec2.Y.ToString() + “,” + new_Vec2.Z.ToString());
    }
    the code means the axis (-1,0,1) rotate 90 degree round the axis (1,0,1) at the point (0,0,0) .I think the result is (1,0,-1),but the output is (0.998497,0.00775,-0.998497),which is not accurate.
    The deviation become bigger and bigger when it caculate again and again.

Leave a Reply to Jeremy TammikCancel reply

Discover more from Autodesk Developer Blog

Subscribe now to keep reading and get access to the full archive.

Continue reading