Following up on the previous post, here is how to put all the pieces together to find a door’s glass area above 2′
- Find the glass area of the door as-is
- Edit the door family
- Create a void to eliminate the bottom 2′ of glass
- Reload the door into the project
- Find the glass area of the modified door
- Rollback the changes to the door and the reloading of the door into the RVT
public void MesaureGlassArea()
{
Document doc = this.ActiveUIDocument.Document;
UIDocument uidoc = new UIDocument(doc);
FamilyInstance instance = doc.GetElement(uidoc.Selection.PickObject(ObjectType.Element)) as FamilyInstance;
// Get the surface area of glass in the family
string info = "Total glass area in door = " + getGlassAreaInFamily(instance) + "\n";
// Open and edit the family document
Document familyDoc = doc.EditFamily(instance.Symbol.Family);
// The family geometry is going to be edited (to create the void to cut off the bottom 2' of glass) and then reloaded into the RVT.
// After this is done, call getGlassAreaInFamily to get the modified glass area.
// But in the end we don't want to keep all the transactions created by CreateExtrusionAndCutGlass.
// A transaction group can be used to rollback all the transactions that were committed inside the transaction group.
using (TransactionGroup transactionGroup = new TransactionGroup(doc,"Glass measurement"))
{
transactionGroup.Start();
// Cut off the bottom 2 feet of glass
CreateExtrusionAndCutGlass(familyDoc);
// Load the modified family back into the RVT
// FamilyLoadOptions tells Revit to overwrite the existing family instances in the RVT
FamilyLoadOptions loadOptions = new FamilyLoadOptions();
familyDoc.LoadFamily(doc,loadOptions);
// Get the surface area of glass in the modified family
info += "Glass in door above 2 feet = " + getGlassAreaInFamily(instance);
// Rollback the transaction group so that the reloading of the family and modifications to the family are discarded.
transactionGroup.RollBack();
}
TaskDialog.Show("Material info", info);
// Close the family document. False means do not save the file
familyDoc.Close(false);
}
private double getGlassAreaInFamily(Element element)
{
foreach (Material material in element.Materials)
{
if (material.Name == "Glass")
return element.GetMaterialArea(material);
}
return 0;
}
// FamilyLoadOptions tells Revit what to do when loading the family into a document that already contains the family
public class FamilyLoadOptions : IFamilyLoadOptions
{
// Always return true so that all existing families and their parameters are overwritten with the new family
public bool OnFamilyFound(bool familyInUse, out bool overwriteParameterValues)
{
overwriteParameterValues = true;
return true;
}
public bool OnSharedFamilyFound(Family sharedFamily, bool familyInUse, out FamilySource source, out bool overwriteParameterValues)
{
overwriteParameterValues = true;
source = FamilySource.Family;
return true;
}
}
private void CreateExtrusionAndCutGlass(Document doc)
{
Autodesk.Revit.ApplicationServices.Application app = doc.Application;
// Height of the void extrusion
double height = 2;
// Four points to define corners of the rectangle to extrude
XYZ pnt1 = new XYZ(-10, -10, 0);
XYZ pnt2 = new XYZ(10, -10, 0);
XYZ pnt3 = new XYZ(10, 10, 0);
XYZ pnt4 = new XYZ(-10, 10, 0);
// Create the four lines of the rectangle
// These are internal "Line" elements, not model lines or detail lines
Line line1 = app.Create.NewLine(pnt1, pnt2, true);
Line line2 = app.Create.NewLine(pnt2, pnt3, true);
Line line3 = app.Create.NewLine(pnt3, pnt4, true);
Line line4 = app.Create.NewLine(pnt4, pnt1, true);
// Put these lines into a CurveArray
CurveArray curveArray = new CurveArray();
curveArray.Append(line1);
curveArray.Append(line2);
curveArray.Append(line3);
curveArray.Append(line4);
// Put this array into a CureArrArray (an array of CurveArrays)
// Extrusion creation uses a CureArrArray so you can extrusion multiple loops
CurveArrArray curveArrayArray = new CurveArrArray();
curveArrayArray.Append(curveArray);
// Create a plane at the origin with a normal in the up direction
XYZ planeNormal = XYZ.BasisZ;
XYZ planeOrigin = XYZ.Zero;
Plane plane = app.Create.NewPlane(planeNormal, planeOrigin);
Extrusion extrusion = null;
using (Transaction t = new Transaction(doc, "Create Extrusion"))
{
t.Start();
// Create a sketch plane to be used for the extrusion
SketchPlane sketchPlane = doc.FamilyCreate.NewSketchPlane(plane);
// Create the extrusion. The "false" specifies that it will be a void
extrusion = doc.FamilyCreate.NewExtrusion(false, curveArrayArray, sketchPlane, height);
t.Commit();
}
// Cut the other 3D elements with the new void using CombineElements and a CombinableElementArray
CombinableElementArray ceArray = new CombinableElementArray();
// Add the new void extrusion to the CombinableElementArray
ceArray.Append(extrusion);
// Add all GenericForm elements with the Glass subcategory to a CombinableElementArray
foreach (GenericForm genericForm in new FilteredElementCollector(doc).OfClass(typeof(GenericForm)).Cast<GenericForm>())
{
Category category = genericForm.Subcategory;
if (category != null && category.Name == "Glass")
{
ceArray.Append(genericForm);
}
}
using (Transaction t = new Transaction(doc, "Combine Elements"))
{
t.Start();
// Combine the elements so that the void will cut the solids
GeomCombination geomCombination = doc.CombineElements(ceArray);
t.Commit();
}
}
Can the IFamilyLoadOptions be used to load a set of edited families in a folder into a model and overwrite? Having a heck of a time figuring it out. I tried the following but Revit crashes. When I put the foreach in a transaction, I get a transaction must be closed before calling LoadFamily error:
UIDocument uidoc = this.ActiveUIDocument;
Document doc = uidoc.Document;
string pathDir = @”C:\Users\Michael\Desktop\MyFamilies”;
string[] array1 = Directory.GetFiles(pathDir, “*.rfa”);
FamilyLoadOptions loadOptions = new FamilyLoadOptions();
foreach (string path in array1)
{
Document familyDoc = Application.OpenDocumentFile(path);
familyDoc.LoadFamily(doc, loadOptions);
familyDoc.Close(false);
}
Thanks.
Use one of the LoadFamily overrides that takes a string (which is the full path to the RFA) and loads it into the active document. For example:
Thanks but it doesn’t seem to work. If the family is already loaded in the project, then it will skip it.
If the families are already loaded you need to use the Document.LoadFamily Method (String, IFamilyLoadOptions, Family%)
The API documentation notes:
If you are reloading an edited family back into the source document from which it was extracted, this method will always fail. This is because this method automatically suppresses the prompts Revit typically uses to deal with conflicts between families, and assumes that any such conflict should prevent the loading. If you want to be able to reload the same family into the source document, you should use the LoadFamily overload accepting IFamilyLoadOptions.
Thanks. I could have sworn i tried something like this but didn’t get it to work but now it is. This is my code:
public void LoadFamilyFolder()
{
Document doc = this.ActiveUIDocument.Document;
IFamilyLoadOptions myOptions = new FamilyOptions();
Family family = null;
using (Transaction t = new Transaction(doc,”Load Families”))
{
t.Start();
foreach (string s in Directory.GetFiles(@”\\ny11\Transfer\myFolder\Project Components”,”*.rfa”))
{
doc.LoadFamily(s, myOptions, out family);
}
t.Commit();
}
}
public class FamilyOptions : IFamilyLoadOptions
{
public bool OnFamilyFound(bool familyInUse, out bool overwriteParameterValues)
{
overwriteParameterValues = true;
return true;
}
public bool OnSharedFamilyFound(Family sharedFamily, bool familyInUse, out FamilySource source, out bool overwriteParameterValues)
{
source = FamilySource.Family;
overwriteParameterValues = true;
return true;
}
}