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:
The command generated a new drafting view showing the wall profile edges like this:
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!
Hi Jeremy,
I'm looking to code a routine to generate a partition/curtain wall that is not vertical. My approach was to generate in a vertical plane derived from an edge, then rotate it into it's orientation about this linear edge. But it seems walls can only be rotated in plan as it doesn't move when I call ElementTransformUtils.RotateElement with a non vertical line. Can you please confirm? Also, if I wish to detach the top of the wall from a level, can you please demonstrate what you would set this parameter to? BuiltInParameter.WALL_TOP_IS_ATTACHED
If there are any other API methods to generate non vertical walls, would be greatly appreciated to find out about them.
Cheers, Jon
Thanks in advance,
Jon
Posted by: JonM | March 08, 2012 at 15:06
Dear Jon,
Nope, I cannot confirm the rotation issue off-hand - it would require some research - so you would have to submit an ADN DevHelp Online case for that.
Regarding your second question:
If you have a parameter taking an ElementId value and wish to set it to undefined or not connected, you can simply use ElementId.InvalidElementId:
http://thebuildingcoder.typepad.com/blog/2011/08/set-underlay-display-property-to-none.html
Cheers, Jeremy.
Posted by: Jeremy Tammik | June 03, 2012 at 15:13