DirectShape
This element type can store arbitrary geometry created by the Revit API or 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.
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 the ApplicationId and ApplicationDataId string parameters that 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:
- Solid (this can be a closed or open shell)
- Mesh
- Curve
- Point
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.
DirectShapeType.SetFamilyName()
provides the ability to set a custom Family name for a DirectShapeType. The validator: DirectShapeType.CanChangeFamilyName()
provides the ability to check if a DirectShapeType supports a custom Family name because certain categories do not support custom Family names.
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 |
|
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 |
|
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 |
|
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 |
|
Reference Curves, Planes, and Points
The methods:
- DirectShape.AddReferenceCurve()
- DirectShape.AddReferencePlane()
- DirectShape.AddReferencePoint()
- DirectShapeType.AddReferenceCurve()
- DirectShapeType.AddReferencePlane()
- DirectShapeType.AddReferencePoint()
enable the creation of reference curves, planes and points inside DirectShape elements. Explicit bounds can be provided for direct shape reference curves and planes. Revit tools that can use named references within families will also be able to select the references inside the DirectShape elements.
The overloads for these methods include an optional DirectShapeReferenceOptions
input. Use DirectShapeReferenceOptions.Name
to set the assigned name for the reference. If the name is specified, it is visible when picking the reference's geometry. Otherwise, the default
DirectShape element name is displayed. The validator DirectShapeReferenceOptions.IsValidReferenceName()
validates the name assigned to DirectShapeReferenceOptions.Name.
The validators:
- DirectShape.IsValidReferenceCurve()
- DirectShape.IsValidReferencePlaneBoundingBoxUV()
- DirectShapeType.IsValidReferenceCurve()
- DirectShapeType.IsValidReferencePlaneBoundingBoxUV()
validates the inputs needed for specifying a plane or curve explicit reference in the DirectShape.
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.
Tagging Direct Shape Geometry
References (such as dimensions) to DirectShape geometry can be maintained when the geometry changes. To do this, add geometry to the DirectShape using the AddExternallyTaggedGeometry
method. The sample below shows how to create an instance of the BRepBuilderPersistentIds
class and use AddSubTag
to build a map of named ExternalGeometryId
's and their corresponding BRepBuilderGeometryId
's. When the geometry is modified, use UpdateExternallyTaggedGeometry
and use the same names for the ExternalGeometryId
's that you will associate with the new geometry.
The CreateDirectShape sample creates a direct shape with a face named "face1". After running this command, manually create a dimension that references this face. Then run ModifyDirectShape to create a new face in the direct shape. Because the old and new faces are both named "face1", the dimension will survive the operation and reference the new face. If you change the ModifyDirectShape code to give the face a different name, then the dimension will fail regeneration with the error that "One or more dimension references are or have become invalid.".
You can also provide an external tag to reference geometry. There is a change in behavior for the following methods. These methods will associate an external ID with the added reference object, if the DirectShapeReferenceOptions
specify one. An exception is thrown if the DirectShape
or DirectShapeType
already has reference geometry with the specified external ID.
- DirectShape.AddReferencePlane()
- DirectShape.AddReferencePoint()
- DirectShape.AddReferenceCurve()
- DirectShapeType.AddReferencePlane()
- DirectShapeType.AddReferencePoint()
- DirectShapeType.AddReferenceCurve()
public DirectShape CreateDirectShape(Document doc)
{
DirectShape ds;
using (Transaction tr = new Transaction(doc, "Create DirectShape"))
{
tr.Start();
ds = DirectShape.CreateElement(doc, new ElementId(BuiltInCategory.OST_GenericModel));
ds.AddExternallyTaggedGeometry(CreateTriangularFace("face1", 1));
tr.Commit();
}
return ds;
}
public void ModifyDirectShape(DirectShape ds)
{
using (Transaction tr = new Transaction(ds.Document, "Update DirectShape"))
{
tr.Start();
ds.UpdateExternallyTaggedGeometry(CreateTriangularFace("face1", 10));
tr.Commit();
}
}
private ExternallyTaggedBRep CreateTriangularFace(string name, double x)
{
BRepBuilder brepBuilder = new BRepBuilder(BRepType.OpenShell);
XYZ pt0 = new XYZ(x, 0, 0);
XYZ pt1 = new XYZ(x, 10, 0);
XYZ pt2 = new XYZ(x, 10, 10);
BRepBuilderGeometryId faceId = brepBuilder.AddFace(BRepBuilderSurfaceGeometry.Create(
Plane.CreateByOriginAndBasis(pt0, XYZ.BasisZ, XYZ.BasisY), null), true);
BRepBuilderGeometryId loopId = brepBuilder.AddLoop(faceId);
brepBuilder.AddCoEdge(loopId, brepBuilder.AddEdge(BRepBuilderEdgeGeometry.Create(pt0, pt1)), false);
brepBuilder.AddCoEdge(loopId, brepBuilder.AddEdge(BRepBuilderEdgeGeometry.Create(pt1, pt2)), false);
brepBuilder.AddCoEdge(loopId, brepBuilder.AddEdge(BRepBuilderEdgeGeometry.Create(pt2, pt0)), false);
brepBuilder.FinishLoop(loopId);
brepBuilder.FinishFace(faceId);
brepBuilder.Finish();
BRepBuilderPersistentIds persistentIds = new BRepBuilderPersistentIds(brepBuilder);
persistentIds.AddSubTag(new ExternalGeometryId(name), faceId);
ExternallyTaggedBRep result = brepBuilder.GetResult(new ExternalGeometryId("MyShape"), persistentIds);
return result;
}