DirectShape

This element type can store arbitrary geometry obtained from import operations or calculations in either a project or family document.

The DirectShape element and related classes support the ability to store externally created geometric shapes in a Revit document. The geometry can include closed solids or meshes. DirectShape is primarily intended for importing shapes from other data formats such as IFC or STEP where not enough information is available to create a "real" Revit element.

A DirectShape object may be assigned a top-level Model category, such as the Wall category. Sub-categories cannot be assigned to DirectShape elements. The IsValidCategoryId() method can test a category id to make sure it is a top-level built-in category approved for use with DirectShape and the Category.CategoryType enumerated value will indicated if the category type is Model. Assigning a category will affect how that object is displayed in Revit and will grant the object a collection of available parameters and some limited behaviors.

DirectShape creation

The static CreateElement() method will create a new instance-level DirectShape. It requires the document in which the DirectShape will be added and the id of an appropriate built-in category. DirectShape provides additional fields that may be used by the creating application to manage the DirectShape elements - two string parameters passed to CreateElement(). These fields can be accessed by the application later if necessary to provide context for the source of the created shape.

Once the DirectShape is created, the shape can be set using one of the overloaded SetShape() method. The shape can be set either directly from a ShapeBuilder object or from a list of GeometryObjects. If you are using a ShapeBuilder object to construct geometry for the DirectShape anyway, there may be a slight performance advantage to using the ShapeBuilder input, as Revit will bypass repetitive validation of the input geometry. It is also possible to append additional geometry objects to the DirectShape using versions of the AppendShape() method. Note that AppendShape() will not merge or join the incoming geometry with any geometry already present, the geometry will be stored independently.

DirectShapes accept the following geometry types as input:

In addition, you can specify geometry to be used in a view-specific representation of a DirectShape. This geometry is input along with the input of a DirectShapeTargetViewType. When setting a view-specific shape representation, it will only be used in views of that type. Currently the only view-specific representation supported is for Plan views.

The following example demonstrates how to create a simple DirectShape from a sphere created using the GeometryCreationUtilities class. Note the use of a reference Frame in creating the geometry. Prior to using it to create geometry, it is good practice to call the static method Frame.CanDefineRevitGeometry() which tests whether the supplied Frame object may be used to define a Revit curve or surface. In order to satisfy the requirements the Frame must be orthonormal and its origin is expected to lie within the Revit design limits. (When creating geometry, it also can be useful to use the static XYZ. IsWithinLengthLimits() to ensure the point is within Revit design limits.)

Code Region: Create a DirectShape

// Create a DirectShape Sphere
public void CreateSphereDirectShape(Document doc)
{
    List<Curve> profile = new List<Curve>();

    // first create sphere with 2' radius
    XYZ center = XYZ.Zero;
    double radius = 2.0;    
    XYZ profile00 = center;
    XYZ profilePlus = center + new XYZ(0, radius, 0);
    XYZ profileMinus = center - new XYZ(0, radius, 0);

    profile.Add(Line.CreateBound(profilePlus, profileMinus));
    profile.Add(Arc.Create(profileMinus, profilePlus, center + new XYZ(radius, 0, 0)));

    CurveLoop curveLoop = CurveLoop.Create(profile);
    SolidOptions options = new SolidOptions(ElementId.InvalidElementId, ElementId.InvalidElementId);

    Frame frame = new Frame(center, XYZ.BasisX, -XYZ.BasisZ, XYZ.BasisY);
    if (Frame.CanDefineRevitGeometry(frame) == true)
    {
        Solid sphere = GeometryCreationUtilities.CreateRevolvedGeometry(frame, new CurveLoop[] { curveLoop }, 0, 2 * Math.PI, options);
        using (Transaction t = new Transaction(doc, "Create sphere direct shape"))
        {
            t.Start();
            // create direct shape and assign the sphere shape
            DirectShape ds = DirectShape.CreateElement(doc, new ElementId(BuiltInCategory.OST_GenericModel));

            ds.ApplicationId = "Application id";
            ds.ApplicationDataId = "Geometry object id";
            ds.SetShape(new GeometryObject[] { sphere });
            t.Commit();
        }
    }
}

The geometry for a DirectShape can also be created using a subclass of the ShapeBuilder class or from a TessellatedShapeBuilder.

ShapeBuilder

ViewShapeBuilder and WireframeBuilder can be used to create geometry to store in a DirectShape class. The ViewShapeBuilder class builds and verifies a view-specific shape representation. It is limited to curve-based representations for plan views. WireframeBuilder constructs a 3D shape representation consisting of points and curves. Both types of ShapeBuilders can be applied to a DirectShape element using the DirectShape.SetShape() or DirectShape.AppendShape() overload that takes a ShapeBuilder parameter.

TessellatedShapeBuilder

TessellatedShapeBuilder can be used create solid, shell, or polymeshes bounded by a set of connected planar facets, created by adding TessellatedFace objects one by one. Faces can only be added to the build while a face set is "open". Use the OpenConnectedFaceSet() method to open a face set. After adding all the TessellatedFaces, call CloseConnectedFaceSet() to close the face set. The builder allows for the possibility of multiple face sets - in such cases the first set should represent the outer 'surface' of a body and all following sets represent interior voids. The builder tries to create a geometry valid in Revit despite inconsistencies or omissions in the input data.

After defining all faces and closing the face set, call the Build() method to build the designated geometrical objects from the stored face sets. The Target, Fallback and GraphicsStyleId properties of the TessellatedShapeBuilder can be set prior to calling Build() or default options will be used. The results of Build() are stored in the TessellatedShapeBuilder and can be retrieved by calling GetBuildResult(). The TessellatedShapeBuilderResult.GetGeometricalObjects() method will return a list of GeometryObjects which can be used with the corresponding DirectShape.SetShape() or DirectShape.AppendShape() overload, as shown in the example below.

Code Region: Create a DirectShape using TessellatedShapeBuilder

// Create a pyramid-shaped DirectShape using given material for the faces
public void CreateTessellatedShape(Document doc, ElementId materialId)
{
    List<XYZ> loopVertices = new List<XYZ>(4);

    TessellatedShapeBuilder builder = new TessellatedShapeBuilder();

    builder.OpenConnectedFaceSet(true);
    // create a pyramid with a square base 4' x 4' and 5' high
    double length = 4.0;
    double height = 5.0;

    XYZ basePt1 = XYZ.Zero;
    XYZ basePt2 = new XYZ(length, 0, 0);
    XYZ basePt3 = new XYZ(length, length, 0);
    XYZ basePt4 = new XYZ(0, length, 0);
    XYZ apex = new XYZ(length / 2, length / 2, height);

    loopVertices.Add(basePt1);
    loopVertices.Add(basePt2);
    loopVertices.Add(basePt3);
    loopVertices.Add(basePt4);
    builder.AddFace(new TessellatedFace(loopVertices, materialId));

    loopVertices.Clear();
    loopVertices.Add(basePt1);
    loopVertices.Add(apex);
    loopVertices.Add(basePt2);
    builder.AddFace(new TessellatedFace(loopVertices, materialId));

    loopVertices.Clear();
    loopVertices.Add(basePt2);
    loopVertices.Add(apex);
    loopVertices.Add(basePt3);
    builder.AddFace(new TessellatedFace(loopVertices, materialId));

    loopVertices.Clear();
    loopVertices.Add(basePt3);
    loopVertices.Add(apex);
    loopVertices.Add(basePt4);
    builder.AddFace(new TessellatedFace(loopVertices, materialId));

    loopVertices.Clear();
    loopVertices.Add(basePt4);
    loopVertices.Add(apex);
    loopVertices.Add(basePt1);
    builder.AddFace(new TessellatedFace(loopVertices, materialId));

    builder.CloseConnectedFaceSet();
    builder.Target = TessellatedShapeBuilderTarget.Solid;
    builder.Fallback = TessellatedShapeBuilderFallback.Abort;
    builder.Build();

    TessellatedShapeBuilderResult result = builder.GetBuildResult();

    using (Transaction t = new Transaction(doc, "Create tessellated direct shape"))
    {
        t.Start();
                
        DirectShape ds = DirectShape.CreateElement(doc, new ElementId(BuiltInCategory.OST_GenericModel));
        ds.ApplicationId = "Application id";
        ds.ApplicationDataId = "Geometry object id";

        ds.SetShape(result.GetGeometricalObjects());
        t.Commit();
    }
}

The image below is the result of running the example above with a concrete material id specified.

BRepBuilder

The BRepBuilder class offers the ability to construct Revit boundary representation geometry (solids, open shells, etc.) as a result of inputs of surfaces, edges, and boundary loops of edges. If the construction of the boundary representation is successful, the resulting geometry objects can be used directly in any other Revit tool that accepts geometry, or the BRepBuilder can be passed directly to populate a DirectShape via the SetShape() and AppendShape() methods of the DirectShape class. Below is an example of using the SetShape() method to assign a cylinder shape to a new DirectShape object.

Code Region: Create a DirectShape using BRepBuilder

private void CreateDirectShapeFromCylinder(Document doc)
{
    // Naming convention for faces and edges: we assume that x is to the left and pointing down, y is horizontal and pointing to the right, z is up
    BRepBuilder brepBuilder = new BRepBuilder(BRepType.Solid);

    // The surfaces of the four faces.
    Frame basis = new Frame(new XYZ(50, -100, 0), new XYZ(0, 1, 0), new XYZ(-1, 0, 0), new XYZ(0, 0, 1));
    CylindricalSurface cylSurf = CylindricalSurface.Create(basis, 50);
    Plane top = Plane.CreateByNormalAndOrigin(new XYZ(0, 0, 1), new XYZ(0, 0, 100));  // normal points outside the cylinder
    Plane bottom = Plane.CreateByNormalAndOrigin(new XYZ(0, 0, 1), new XYZ(0, 0, 0)); // normal points inside the cylinder
                                                                                              
    // Add the four faces
    BRepBuilderGeometryId frontCylFaceId = brepBuilder.AddFace(BRepBuilderSurfaceGeometry.Create(cylSurf, null), false);
    BRepBuilderGeometryId backCylFaceId = brepBuilder.AddFace(BRepBuilderSurfaceGeometry.Create(cylSurf, null), false);
    BRepBuilderGeometryId topFaceId = brepBuilder.AddFace(BRepBuilderSurfaceGeometry.Create(top, null), false);
    BRepBuilderGeometryId bottomFaceId = brepBuilder.AddFace(BRepBuilderSurfaceGeometry.Create(bottom, null), true);

    // Geometry for the four semi-circular edges and two vertical linear edges
    BRepBuilderEdgeGeometry frontEdgeBottom = BRepBuilderEdgeGeometry.Create(Arc.Create(new XYZ(0, -100, 0), new XYZ(100, -100, 0), new XYZ(50, -50, 0)));
    BRepBuilderEdgeGeometry backEdgeBottom = BRepBuilderEdgeGeometry.Create(Arc.Create(new XYZ(100, -100, 0), new XYZ(0, -100, 0), new XYZ(50, -150, 0)));

    BRepBuilderEdgeGeometry frontEdgeTop = BRepBuilderEdgeGeometry.Create(Arc.Create(new XYZ(0, -100, 100), new XYZ(100, -100, 100), new XYZ(50, -50, 100)));
    BRepBuilderEdgeGeometry backEdgeTop = BRepBuilderEdgeGeometry.Create(Arc.Create(new XYZ(0, -100, 100), new XYZ(100, -100, 100), new XYZ(50, -150, 100)));

    BRepBuilderEdgeGeometry linearEdgeFront = BRepBuilderEdgeGeometry.Create(new XYZ(100, -100, 0), new XYZ(100, -100, 100));
    BRepBuilderEdgeGeometry linearEdgeBack = BRepBuilderEdgeGeometry.Create(new XYZ(0, -100, 0), new XYZ(0, -100, 100));

    // Add the six edges
    BRepBuilderGeometryId frontEdgeBottomId = brepBuilder.AddEdge(frontEdgeBottom);
    BRepBuilderGeometryId frontEdgeTopId = brepBuilder.AddEdge(frontEdgeTop);
    BRepBuilderGeometryId linearEdgeFrontId = brepBuilder.AddEdge(linearEdgeFront);
    BRepBuilderGeometryId linearEdgeBackId = brepBuilder.AddEdge(linearEdgeBack);
    BRepBuilderGeometryId backEdgeBottomId = brepBuilder.AddEdge(backEdgeBottom);
    BRepBuilderGeometryId backEdgeTopId = brepBuilder.AddEdge(backEdgeTop);

    // Loops of the four faces
    BRepBuilderGeometryId loopId_Top = brepBuilder.AddLoop(topFaceId);
    BRepBuilderGeometryId loopId_Bottom = brepBuilder.AddLoop(bottomFaceId);
    BRepBuilderGeometryId loopId_Front = brepBuilder.AddLoop(frontCylFaceId);
    BRepBuilderGeometryId loopId_Back = brepBuilder.AddLoop(backCylFaceId);

    // Add coedges for the loop of the front face
    brepBuilder.AddCoEdge(loopId_Front, linearEdgeBackId, false);
    brepBuilder.AddCoEdge(loopId_Front, frontEdgeTopId, false);
    brepBuilder.AddCoEdge(loopId_Front, linearEdgeFrontId, true);
    brepBuilder.AddCoEdge(loopId_Front, frontEdgeBottomId, true);
    brepBuilder.FinishLoop(loopId_Front);
    brepBuilder.FinishFace(frontCylFaceId);

    // Add coedges for the loop of the back face
    brepBuilder.AddCoEdge(loopId_Back, linearEdgeBackId, true);
    brepBuilder.AddCoEdge(loopId_Back, backEdgeBottomId, true);
    brepBuilder.AddCoEdge(loopId_Back, linearEdgeFrontId, false);
    brepBuilder.AddCoEdge(loopId_Back, backEdgeTopId, true);
    brepBuilder.FinishLoop(loopId_Back);
    brepBuilder.FinishFace(backCylFaceId);

    // Add coedges for the loop of the top face
    brepBuilder.AddCoEdge(loopId_Top, backEdgeTopId, false);
    brepBuilder.AddCoEdge(loopId_Top, frontEdgeTopId, true);
    brepBuilder.FinishLoop(loopId_Top);
    brepBuilder.FinishFace(topFaceId);

    // Add coedges for the loop of the bottom face
    brepBuilder.AddCoEdge(loopId_Bottom, frontEdgeBottomId, false);
    brepBuilder.AddCoEdge(loopId_Bottom, backEdgeBottomId, false);
    brepBuilder.FinishLoop(loopId_Bottom);
    brepBuilder.FinishFace(bottomFaceId);

    brepBuilder.Finish();

    using (Transaction tr = new Transaction(doc, "Create a DirectShape"))
    {
        tr.Start();
        DirectShape ds = DirectShape.CreateElement(doc, new ElementId(BuiltInCategory.OST_GenericModel));
        ds.SetShape(brepBuilder);
        tr.Commit();
    }
}

ShapeImporter

The ShapeImporter utility class supports conversion of geometry stored in external formats (such as SAT and Rhino) into a collection of GeometryObjects which can be used to set the shape for a DirectShape. Use ShapeImporter.Convert() to generate the geometry objects (and where possible, corresponding materials and graphics styles in the associated document).

Code Region: Create a DirectShape from SAT file

public void ReadSATFile(Document revitDoc)
{
    // Allow the user to select a SAT file.
    OpenFileDialog ofd = new OpenFileDialog();
    ofd.Filter = "SAT Files (*.sat)|*.sat";

    if (DialogResult.OK == ofd.ShowDialog())
    {
        ShapeImporter shapeImporter = new ShapeImporter();
        shapeImporter.InputFormat = ShapeImporterSourceFormat.SAT; 
        IList<GeometryObject> shapes = shapeImporter.Convert(revitDoc, ofd.FileName);

        if (shapes.Count != 0)
        {
            using (Transaction tr = new Transaction(revitDoc, "Create a DirectShape"))
            {
                tr.Start();

                DirectShape dsImportedSat = DirectShape.CreateElement(revitDoc, new ElementId(BuiltInCategory.OST_Walls));
                dsImportedSat.SetShape(shapes);

                tr.Commit();
            }
        }
    }
}

Options

The DirectShapeOptions class is used to control behavior of a DirectShape object. Use DirectShape.SetOptions() to set the options used by a DirectShape object. The GetOptions() method will return the DirectShapeOptions currently used by the DirectShape object.

DirectShape elements, by default, support element references, including dimensions, alignments, and face hosting, as well as snapping. This default behavior can be changed using the DirectShapeOptions.ReferencingOption property. If it is set to NotReferenceable, the geometry may not be used for dimensioning, snapping, alignment, or face-hosting. The element may still be selected by the user for operations which do not reference individual geometry objects.

DirectShape elements also support the ability to participate in room boundary calculations, if they are of an appropriate category for room boundary calculations, and if the associated "Room Bounding" parameter is set to true. The property DirectShapeOptions.RoomBoundingOption identifies whether the DirectShape supports an option for the "Room Bounding" parameter to permit participation in room boundary calculations. The default value is NotApplicable, but this will be changed automatically to SetByParameter for applicable DirectShapes.