0671/2078

Planar Face Transform

Originally Published in

Here is another contribution by Joe Offord of Enclos, who already shared valuable insights on accessing curtain wall geometry, speeding up the interactive selection process, mirroring in a new family and changing the active view.

Above all, he implements all his stuff in VB, so this is also something for you VB aficionados.

This time, Joe looks at a command that redraws a planar face’s edges in a drafting view. The issue is somewhat related to the discussion of polygon transformations.

However, instead of using rotations and translations, which can be difficult to determine in 3D, Joe wrote a utility function that re-maps the global coordinate system to the planar face’s coordinate system using vectors and origins.

The command prompts the user to select a planar face on an elements, creates a new drafting view, retrieves the edges of the selected face, and transforms them from the 3D space to the drafting view.

To test this, I selected the following slightly lopsided wall in 3D:

Wall in 3D

The command generated a new drafting view showing the wall profile edges like this:

Transformed wall profile in drafting view

Here is the Execute method implementation for the mainline of the command:

  Public Function Execute( _
    ByVal commandData As ExternalCommandData, _
    ByRef message As String, _
    ByVal elements As ElementSet) As Result Implements IExternalCommand.Execute
 
    Dim uiapp As UIApplication = commandData.Application
    Dim app As Application = uiapp.Application
    Dim uidoc As UIDocument = uiapp.ActiveUIDocument
    Dim doc As Document = uidoc.Document
    Dim sel As Selection = uidoc.Selection
 
    Try
      Dim ref As Reference = sel.PickObject( _
        ObjectType.Face, "Select a face")
 
      Dim elem As Element = doc.GetElement(ref)
 
      Dim gObj As GeometryObject _
        = elem.GetGeometryObjectFromReference(ref)
 
      Dim face As PlanarFace = TryCast(gObj, PlanarFace)
 
      If face Is Nothing Then
        MsgBox("Not a planar face")
      End If
 
      Dim v As ViewDrafting = Nothing
 
      Dim tr As New Transaction(doc, "Draw Face")
      tr.Start()
 
      Try
        v = doc.Create.NewViewDrafting 'create a new view
        v.Scale = 48 '1/4" = 1'-0"
 
        'this transform re-orients the global 
        'coordinate system to the face's coordinate system
        Dim trans As Transform _
          = Util.PlanarFaceTransform(face)
 
        For Each eArr As EdgeArray In face.EdgeLoops
          For Each e As Edge In eArr
            Dim c As Curve = e.AsCurveFollowingFace(face)
            c = c.Transformed(trans) 'orient the curve on the XY plane
            doc.Create.NewDetailCurve(v, c) 'draw the curve
          Next
        Next
 
        tr.Commit()
      Catch ex As Exception
        tr.RollBack()
        MsgBox("Error: " + ex.Message)
      End Try
 
      If tr.GetStatus = TransactionStatus.Committed Then
        uidoc.ActiveView = v
      End If
 
    Catch ex As Exception
      MsgBox("Error: " + ex.Message)
    End Try
 
    Return Result.Succeeded
 
  End Function

The transformation from the planar face coordinate system is created by the following two utility methods:

  ''' <summary>
  ''' Return a transform that changes a x,y,z 
  ''' coordinate system to a new x',y',z' system
  ''' </summary>
  Public Shared Function TransformByVectors( _
    ByVal oldX As XYZ, _
    ByVal oldY As XYZ, _
    ByVal oldZ As XYZ, _
    ByVal oldOrigin As XYZ, _
    ByVal newX As XYZ, _
    ByVal newY As XYZ, _
    ByVal newZ As XYZ, _
    ByVal newOrigin As XYZ) As Transform
 
    ' [new vector] = [transform]*[old vector]
    ' [3x1] = [3x4] * [4x1]
    '
    ' [v'x]   [ i*i'  j*i'  k*i'  translationX' ]   [vx]
    ' [v'y] = [ i*j'  j*j'  k*j'  translationY' ] * [vy]
    ' [v'z]   [ i*k'  j*k'  k*k'  translationZ' ]   [vz]
    '                                               [1 ]
    Dim t As Transform = Transform.Identity
 
    Dim xx As Double = oldX.DotProduct(newX)
    Dim xy As Double = oldX.DotProduct(newY)
    Dim xz As Double = oldX.DotProduct(newZ)
 
    Dim yx As Double = oldY.DotProduct(newX)
    Dim yy As Double = oldY.DotProduct(newY)
    Dim yz As Double = oldY.DotProduct(newZ)
 
    Dim zx As Double = oldZ.DotProduct(newX)
    Dim zy As Double = oldZ.DotProduct(newY)
    Dim zz As Double = oldZ.DotProduct(newZ)
 
    t.BasisX = New XYZ(xx, xy, xz)
    t.BasisY = New XYZ(yx, yy, yz)
    t.BasisZ = New XYZ(zx, zy, zz)
 
    ' The movement of the origin point 
    ' in the old coordinate system
 
    Dim translation As XYZ = newOrigin - oldOrigin
 
    ' Convert the translation into coordinates 
    ' in the new coordinate system
 
    Dim translationNewX As Double _
      = xx * translation.X _
        + yx * translation.Y _
        + zx * translation.Z
 
    Dim translationNewY As Double _
      = xy * translation.X _
        + yy * translation.Y _
        + zy * translation.Z
 
    Dim translationNewZ As Double _
      = xz * translation.X _
        + yz * translation.Y _
        + zz * translation.Z
 
    t.Origin = New XYZ( _
      -translationNewX, _
      -translationNewY, _
      -translationNewZ)
 
    Return t
 
  End Function
 
  Public Shared Function PlanarFaceTransform( _
    ByVal face As PlanarFace) As Transform
 
    Return Util.TransformByVectors( _
      XYZ.BasisX, _
      XYZ.BasisY, _
      XYZ.BasisZ, _
      XYZ.Zero, _
      face.Vector(0), _
      face.Vector(1), _
      face.Normal, _
      face.Origin)
  End Function

Here is PlanarFaceTransform.zip including the complete source code and Visual Studio solution of this command.

Many thanks to Joe for sharing this!