Issue
In the UI, you can control the adjustment of cutbacks between two framing structures, such as beams and columns. (i.e., Select a beam or a column >> contextual "Modify | Structural Framing" tab >> "Geometry" panel >> "Beam/Column Joins" button, which is a rather shy looking little icon located at the center of the panel.)
How can we accomplish the same using the API?
Solution
FamilyInstance has a property called ExtensionUtility. You can modify this property to set to desired join types. e.g.,
IExtension ie1 = girder1.ExtensionUtility;
if (ie1 != null)
{
// This sets cutback join
ie1.set_Extended(0, false);
ie1.set_Extended(1, false);
// This extends the ends.
//ie1.set_Extended(0, true);
//ie1.set_Extended(1, true);
}
where the first argument of the Extended is the index of two ends, which is either 0 or 1. The second indicates if the end is extended or not (if not, it means cutback).
Below is a sample code that demonstrates the usage of this property.
The sample creates two set of girders and place them in circular shapes: one with cutbacks at both ends. The other with miter. When Extended property is false, it creates a cutback. If Extended is true for two ends, they create a miter end. One little caveat I found is that Revit tries to adjust those join conditions automatically when you first add elements to the document. To override this behavior, you will need to set join conditions after girders were added to the document to make sure that your setting is respected. The resulting configuration is shown in the picture at the bottom.
Note: helper functions, FindFamilyType and FindElement, are same as the ones in our training labs, which you can find on our public ADN site under Samples and Documentation section.
// Create a set of girders and place them in a circular shape,
// and set the join type (i.e., cutback and miter).
[Transaction(TransactionMode.Manual)]
public class rvtCmd_CreateBeamCircle : IExternalCommand
{
// Member variables
Application m_rvtApp;
Document m_rvtDoc;
public Result Execute(
ExternalCommandData commandData,
ref string message,
ElementSet elements)
{
// Get the access to the top most objects.
UIApplication rvtUIApp = commandData.Application;
UIDocument rvtUIDoc = rvtUIApp.ActiveUIDocument;
m_rvtApp = rvtUIApp.Application;
m_rvtDoc = rvtUIDoc.Document;
// Find a family type called "M_W-Wide Flange: W310X38.7"
// Hard coding for simplicity.
// Try with a metric construction template, for example.
const string GirderFamilyName = "M_W-Wide Flange";
const string GirderFamilyTypeName = "W310X38.7";
FamilySymbol girderType =
(FamilySymbol)Utils.FindFamilyType(
m_rvtDoc, typeof(FamilySymbol),
GirderFamilyName, GirderFamilyTypeName,
BuiltInCategory.OST_StructuralFraming);
if (girderType == null)
{
TaskDialog.Show(
"CreateBeamCircle",
"Please load: " +
GirderFamilyName + ": " + GirderFamilyTypeName);
return Result.Failed;
}
// Find "Level 1"
Level level1 =
(Level)Utils.FindElement(
m_rvtDoc, typeof(Level), "Level 1");
// Create a set of girders with cutback
double radius = 8.0;
bool join = false; // cutback
CreateGirderCircle(girderType, level1, radius, join);
// This time, create a set of girders with miter
radius = 6.0;
join = true; // miter
CreateGirderCircle(girderType, level1, radius, join);
return Result.Succeeded;
}
// Create a set of girders and form a circular shape
public bool CreateGirderCircle(
FamilySymbol girderType, Level level1,
double radius, bool join)
{
Transaction tr =
new Transaction(m_rvtDoc, "Create Girder");
tr.Start();
// Create a set of girders and place them in circle
int div = 20; // # of division
double th = Math.PI * 2.0/(double)div; // angle per girder
double ang = 0.0;
double z = level1.Elevation;
XYZ pt1 = new XYZ(radius, 0.0, z);
List<FamilyInstance> girders = new List<FamilyInstance>();
for (int i = 1; i <= div; i++)
{
ang = th * (double)i;
double x = Math.Cos(ang) * radius;
double y = Math.Sin(ang) * radius;
XYZ pt2 = new XYZ(x, y, z);
// Create each girder
FamilyInstance girder =
CreateGirder(pt1, pt2, girderType, level1);
girders.Add(girder);
pt1 = pt2;
}
m_rvtDoc.Regenerate();
tr.Commit();
// Loop through the list of girders we created and
// set the join type. We are doing this as a separate
// loop because Revit automatically controls joins
// when beams are added to the doc
Transaction tr2 =
new Transaction(m_rvtDoc, "Join Girder");
tr2.Start();
// ExtensionUtility property controls the adjustment of
// cutback.
// i.e., Modify tab >> Edit Geometry >> Beam/Column Join
//
// IExtension.Extended(index, isCutback)
// index: 0 or 1, indicating an end point.
// isCutback: indicates if we can to cutback or not.
// If two beams with no cutback meet, they will be
// miterd.
IExtension ie1 = girders.Last().ExtensionUtility;
for (int i = 0; i < div; i++)
{
IExtension ie2 = girders[i].ExtensionUtility;
if (ie1 != null && ie2 != null)
{
// join = false: cutback, true: miter
ie1.set_Extended(1, join);
ie2.set_Extended(0, join);
}
ie1 = ie2;
}
m_rvtDoc.Regenerate();
// Finally commit
tr2.Commit();
return true;
}
// Create each girder with a given family type at
// the given location.
private FamilyInstance CreateGirder(
XYZ pt1, XYZ pt2, FamilySymbol symbol, Level level)
{
// Create a girder
Line line = m_rvtApp.Create.NewLineBound(pt1, pt2);
FamilyInstance girder =
m_rvtDoc.Create.NewFamilyInstance(
line, symbol, level, StructuralType.Beam);
return girder;
}
}
Here is the results. The outer circle shows cutback at both ends. The inner circle shows miter joins.