We’re always trying to automate tasks, it’s actually a common request when the adoption of tools increase and the amount of work follow it. As we know, this is a great area for API: the machine is amazing doing simple math and repetitive tasks.
This time the discussion was based on daily challenges at MHA, a Brazilian engineering firm localized in São Paulo. They are/were involved in several important projects, especially in MEP discipline, including the business center where Autodesk is located here in São Paulo.
Image source: Wikipedia
One task currently consuming a long time is around placing light families on a Revit project. Of course the whole process cannot be automated as is based on the engineer’s analysis and experience, but the initial step of loading and placing the required number can be speed up.
Here are the steps we identifies:
- Select a family (.rfa) file that has a Light fixture. (in this code, the category of the family is not checked)
- Choose one symbol on the family just loaded
- Ask user to select reference plane, space and a pick a box on the project. These are used to calculate the number and location of the families.
- Do some basic math (but yet not so simple logic) to find where to place the instances
- Finally, in a loop, place all family instances.
This is not intended to replace the engineer, of course not. But by placing all the instance needed, the engineer can now move and adjust it, knowing the lumens requirement is meet (number of lights/lumens per room/space).
Let’s see the code, please follow the comments.
UIApplication uiapp = commandData.Application;UIDocument uidoc = uiapp.ActiveUIDocument;Application app = uiapp.Application;Document doc = uidoc.Document; // ask user to select a family fileSystem.Windows.Forms.OpenFileDialog fileDlg = new System.Windows.Forms.OpenFileDialog();fileDlg.Filter = "Revit Family (*.rfa)|*.rfa";fileDlg.Multiselect = false;if (fileDlg.ShowDialog() != System.Windows.Forms.DialogResult.OK) return Result.Cancelled; // load the selected familyFamily fam;if (!doc.LoadFamily(fileDlg.FileName, out fam)) return Result.Failed; // select the symbol on the loaded family// this is a simple WindowsForm with a // ComboBox and a Button (action OK)FormSelSymbol frm = new FormSelSymbol();foreach (ElementId symbolId in fam.GetFamilySymbolIds()){ FamilySymbol s = doc.GetElement(symbolId) as FamilySymbol; frm.cboSymbols.Items.Add(s);}// as the FamilySymbol is stored on the combo box// let's make it show the Name propertyfrm.cboSymbols.DisplayMember = "Name";if (frm.ShowDialog() != System.Windows.Forms.DialogResult.OK) return Result.Cancelled;// and get the selected itemFamilySymbol symbol = frm.cboSymbols.SelectedItem as FamilySymbol; // for this case, we'll first select the reference planeElementId refPlaneId;ReferencePlane refPlane;try{ refPlaneId = uidoc.Selection.PickObject(ObjectType.Element, new GenericFilter<ReferencePlane>(), "Select reference plance").ElementId; refPlane = doc.GetElement(refPlaneId) as ReferencePlane;}catch (Autodesk.Revit.Exceptions.OperationCanceledException){ return Result.Cancelled; // clean command exit} // space to get lumens dataSpace space;try{ ElementId id = uidoc.Selection.PickObject(ObjectType.Element, new GenericFilter<Space>(), "Select space").ElementId; space = doc.GetElement(id) as Space;}catch (Autodesk.Revit.Exceptions.OperationCanceledException){ return Result.Cancelled; // clean command exit} // ask user to select a pick box where the lights // will be created (along the reference planePickedBox box;try{ box = uidoc.Selection.PickBox(PickBoxStyle.Crossing, "Select pick box");}catch (Autodesk.Revit.Exceptions.OperationCanceledException){ return Result.Cancelled; // clean command exit} // now it's time for some math// let's calculate the axis where // the lights will be createddouble h = box.Max.X - box.Min.X;double w = box.Max.Y - box.Min.Y;Line axis;if (h > w) axis = Line.CreateBound( new XYZ( box.Min.X, (box.Max.Y - box.Min.Y) / 2 + box.Min.Y, refPlane.BubbleEnd.Z), new XYZ( box.Max.X, (box.Max.Y - box.Min.Y) / 2 + box.Min.Y, refPlane.BubbleEnd.Z));else axis = Line.CreateBound( new XYZ( (box.Max.X - box.Min.X) / 2 + box.Min.X, box.Min.Y, refPlane.BubbleEnd.Z), new XYZ( (box.Max.X - box.Min.X) / 2 + box.Min.X, box.Max.Y, refPlane.BubbleEnd.Z)); // now calculate the number of required lights// first the Luminous of each light (from the family symbol)double lightLuminous = symbol.get_Parameter( BuiltInParameter.FBX_LIGHT_LIMUNOUS_FLUX).AsDouble();// and get the information from the Space// assuming it was assigned as a Space Style paramdouble requiredLuminous = doc.GetElement( space.get_Parameter("Space Style").AsElementId()) .get_Parameter("Luminous").AsDouble(); // now the number of lightsint numerOfLights = (int)Math.Round((requiredLimunous / lightLimunous));// and the distance between them double distanceBetweenLights = axis.ApproximateLength / (numerOfLights + 2); // all set, time to add the lights!// place family instances at the locationsfor (int p = 1; p <= numerOfLights; p++){ // evaluate a point along the axis // remember the Evaluate can be parametric, therefore // normalized between 0 and 1 XYZ pointOnAxis = axis.Evaluate( p * distanceBetweenLights / axis.ApproximateLength, true); doc.Create.NewFamilyInstance( refPlane.Reference, pointOnAxis, new XYZ(0, 0, 0), symbol);} return Result.Succeeded;
And here is the GenericFilter class used on this code, a simple class that implements the filter interface
public class GenericFilter<T> : ISelectionFilter{ public bool AllowElement(Element elem) { return (elem is T); } public bool AllowReference(Reference reference, XYZ position) { return true; }}

Leave a Reply