#RTCNA Wish 6: Save all groups to file

In response to a tweet asking to save all groups, I responded that there is no API for Save Group. That’s true, but all hope is not lost.

This video shows how the API can be used to generate a text file listing all model groups. Then this text file is used as input to a journal file that saves each group to its own file.

Advertisements

#RTCNA Wish 5: Delete (almost) all revisions

Scott sent a tweet about deleting all revisions from a project. The tricky part is that, just like how the UI grays out the Delete button when there is only one revision in the project, we can’t use the API to delete the last revision.
Capture
This code uses the Revit 2015 API because the Revision class is new to 2015.

public void DeleteRevisions()
{
    Document doc = this.ActiveUIDocument.Document;

    // get list of all revisions in the document
    ICollection<ElementId> elementIds = new FilteredElementCollector(doc)
        .OfClass(typeof(Revision))
        .ToElementIds();
    
    // Remove the first revision from the list
    // Revit must have one revision in the document, so we can't delete them all
    elementIds.Remove(elementIds.First());

    using (Transaction t = new Transaction(doc,"Delete Revisions"))
    {
        t.Start();
        doc.Delete(elementIds);
        t.Commit();
    }
}

#RTNCA Wish 4: Dimension creation

Last night at the API Open House, we talked about the power and limitations of the Dimensions API.

This example shows how to create dimensions that reference two parallel faces. The video shows running the command in a locked 3D view and selecting the end faces of beams. It is also necessary to set the view’s workplane so that the direction of the dimension lies in this plane.

More automated creation of these dimensions could be achieved by programatically getting the faces from the beams so that the user need only select the beam instead of the 3 faces shown in this sample.

public void CreateDimFromFaces()
{
    Document doc = this.ActiveUIDocument.Document;
    UIDocument uidoc = this.ActiveUIDocument;
    Reference r1 = uidoc.Selection.PickObject(ObjectType.Face, "Pick face for dimension reference");
    Reference r2 = uidoc.Selection.PickObject(ObjectType.Face, "Pick face for dimension reference");
    Reference r3 = uidoc.Selection.PickObject(ObjectType.Face, "Pick face for view work plane");
    ReferenceArray ra = new ReferenceArray();
    ra.Append(r1);
    ra.Append(r2);
    Element e = doc.GetElement(r2);
    Location l = e.Location;
    LocationCurve lc = l as LocationCurve;
    Curve curve = lc.Curve;
    Line line = curve as Line;
    
    using (Transaction t = new Transaction(doc,"dim"))
    {
        t.Start();
        doc.ActiveView.SketchPlane = SketchPlane.Create(doc, r3);
        Dimension dim = doc.Create.NewDimension(doc.ActiveView, line, ra);
        t.Commit();
    }
}

#RTCNA Wish 3: Copy rooms from one phase to another

Matt and some others asked how to “Copy All Rooms/Spaces to all phases in project”. This example shows how we can specify a “source phase” and create new rooms for each of those rooms in a “target phase”.

public void copyRoomPhase()
{
    string sourcePhaseName = "New Construction";
    string targetPhaseName = "Existing";
    
    Document doc = this.ActiveUIDocument.Document;
    
    Phase targetPhase = new FilteredElementCollector(doc)
        .OfClass(typeof(Phase))
        .Cast<Phase>()
        .FirstOrDefault(q => q.Name == targetPhaseName);

    Phase sourcePhase = new FilteredElementCollector(doc)
        .OfClass(typeof(Phase))
        .Cast<Phase>()
        .FirstOrDefault(q => q.Name == sourcePhaseName);
    
    IList<Element> roomList = new FilteredElementCollector(doc)
        .OfClass(typeof(SpatialElement))
        .OfCategory(BuiltInCategory.OST_Rooms).ToList();
    
    PlanTopologySet planTopologies = null;
    using (Transaction t = new Transaction(doc,"get topology"))
    {
        t.Start();
        planTopologies = doc.PlanTopologies;
        t.Commit();
    }

    foreach (Element sourceRoom in roomList)
    {
        XYZ pointInSourceRoom = ((LocationPoint)(sourceRoom.Location)).Point;
        
        Parameter phaseParam = sourceRoom.get_Parameter("Phase");
        if (doc.GetElement(phaseParam.AsElementId()).Name != sourcePhaseName)
            continue;
        
        Room newRoom = null;
        using (Transaction t = new Transaction(doc,"New Room"))
        {
            t.Start();
            newRoom = doc.Create.NewRoom(targetPhase);
            t.Commit();
        }
        
        bool found = false;
        
        foreach (PlanTopology pt in planTopologies)
        {
            if (found)
                break;
            
            foreach (PlanCircuit pc in pt.Circuits.Cast<PlanCircuit>())
            {
                using (Transaction t = new Transaction(doc,"Place Room"))
                {
                    t.Start();
                    Room placedNewRoom = doc.Create.NewRoom(newRoom,pc);
                    doc.Regenerate();
                    
                    if (placedNewRoom.IsPointInRoom(pointInSourceRoom))
                    {
                        t.Commit();
                        found = true;
                        break;
                    }
                    else
                    {
                        t.RollBack();
                    }
                }
            }
        }
    }
}

#RTCNA Wish 2 granted: Find pinned elements

Steve wishes for an easy way to use the API to find pinned elements. Wish granted!

public void findPinned()
{
    UIDocument uidoc = this.ActiveUIDocument;
    Document doc = this.ActiveUIDocument.Document;
    
    // create new SelElementSet to store elements that will be selected
    SelElementSet selSet = SelElementSet.Create();
    
    // find all elements in the active view that are pinned
    foreach (Element e in new FilteredElementCollector(doc, doc.ActiveView.Id).WhereElementIsNotElementType().Where(q => q.Pinned))
    {
        // add each of these elements to the set
        selSet.Add(e);
    }
    
    // select all elements in the set
    uidoc.Selection.Elements = selSet;
    
    // refresh the view so that the newly selected elements are highlighted
    uidoc.RefreshActiveView();
    
    TaskDialog.Show("Elements Pinned","Elements pinned in view '" + doc.ActiveView.Name + "' = " + selSet.Size.ToString());
}

RTCNA Wish #1: Replace detail groups with detail components

Michael McCune sent a wish via Twitter to “replace detail group instances with a detail component”.

That’s a good wish, and one that can almost be completely granted via the API as shown in this video.

The limitation is that the API provides no way to identify the rotation angle of a group instance. For any element in a subclass of the Instance class (such as a family instance, import instance, or Revit link instance) the API provides GetTransform() which contains the data needed to determine the instance’s rotation angle. The AssemblyInstance class also implements GetTransform().

Unfortunately, the Group class has no such method. As a result, while we could rotate the newly created detail components, there is no way to query the detail group instance to determine how much each detail component should be rotated.

public void detailGroupToComponent()
{
    Document doc = this.ActiveUIDocument.Document;
    
    // create a dictionary to store the mapping between detail groups and detail component
    // the NewLine character is used to delimit the family name and family type name
    Dictionary<string, string> groupComponentDict = new Dictionary<string, string>();
    groupComponentDict.Add("DG1","DetailComponent1" + Environment.NewLine + "Type 1");
    groupComponentDict.Add("filled regions","DetailComponent2" + Environment.NewLine + "Type 2");
    
    using (Transaction t = new Transaction(doc,"Detail Groups -> Detail Components"))
    {
        t.Start();    
        
        // find all group instances in the document with the Detail Groups category
        IEnumerable<ElementId> groups =  new FilteredElementCollector(doc)
            .OfClass(typeof(Group))
            .OfCategory(BuiltInCategory.OST_IOSDetailGroups).ToElementIds();
        
        foreach (ElementId id in groups)
        {
            Group g = doc.GetElement(id) as Group;
            
            // get the XYZ location of the group instance
            Location location = g.Location;
            LocationPoint lp = location as LocationPoint;
            XYZ xyz = lp.Point;
            
            // get the view that owns the group instance
            ElementId ownerviewId = g.OwnerViewId;
            View ownerview = doc.GetElement(ownerviewId) as View;
            
            // lookup the name of the family that will replace this group instance
            string familyNameType = groupComponentDict[g.Name];
            
            // split the family name / family type string
            string[] split = familyNameType.Split(new string[] {Environment.NewLine}, StringSplitOptions.None);
            string famName = split[0];
            string typeName = split[1];
            
            // find the FamilySymbol (which is the API name for the Family Type)
            FamilySymbol fs = new FilteredElementCollector(doc)
                .OfClass(typeof(FamilySymbol))
                .Cast<FamilySymbol>()
                .FirstOrDefault(q => q.Family.Name == famName && q.Name == typeName);

            // delete the group instance
            doc.Delete(g.Id);
            
            // create an instance of the detail component
            doc.Create.NewFamilyInstance(xyz, fs, ownerview);
        }
        t.Commit();
    }
}

Get your #RTCNA Revit API Wishes Ready!

Like last year, during the upcoming few days of Revit Technology Conference North America, please tweet your Revit API questions and wishes to me at twitter.com/BoostYourBIM. I will do my best to reply to as many as possible with code, videos, and blog posts showing how we can use the API to make Revit better.

Revit disappears after startup with a Windows 8.1 Clean Boot

Has anyone ever seen something like this?

I was having some problems with Windows (like Windows Explorer freezing when I try to create a new folder). So I did a Clean Boot and used shexview.exe to disable the non-Microsoft entries.

These changes seem to have fixed the problems I was having with Chrome and Windows Explorer, but now I can’t run Revit. Revit starts, the startup page briefly appears, then the Revit session dissapears. Does anyone know what might be happening? I’d really appreciate your help!

The last lines of the journal file are:

Jrn.Command “Internal” , “Show or hide recent files , ID_STARTUP_PAGE”
‘ 0:< ::1:: Delta VM: Avail -26 -> 134216371 MB, Used 217 MB; RAM: Avail -3 -> 5496 MB, Used +2 -> 310 MB
‘C 09-Jun-2014 22:19:25.709; 0:< <-processShellCommand
‘ 0:< ->DesktopMFCApp::doStartupWarnings
‘C 09-Jun-2014 22:19:25.710; 0:< manage licensing
‘ 0:< ::1:: Delta VM: Avail -85 -> 134216287 MB, Used +8 -> 226 MB, Peak +0 -> 226 MB; RAM: Avail -47 -> 5450 MB, Used +34 -> 345 MB, Peak +29 -> 345 MB
‘C 09-Jun-2014 22:19:28.617; 0:< License checkout failed: 20
‘C 09-Jun-2014 22:19:28.618; 0:< License cleanup skipped

Process Monitor shows:

Capture

UPDATE:  This checkbox for FLEXnet needed to be turned back on. Problem solved thanks to the suggestion from Luke Johnson.

Capture

Maintaining order & preventing #Revit chaos

In a few weeks I will be speaking at Revit Technology Conference in Chicago about how the Revit API can help you maintain order and prevent chaos in your Revit adventures.

Techniques such as “using Worksets as a security guard” can work, but when you have 100s or 1000s of person-months of effort invested in your 400MB RVT file, you might want a much more complete set of tools to monitor, audit, and protect your model.

It would be great to have your input to shape this course (and related posts on this blog). Please leave your comments on this post or contact me at https://boostyourbim.wordpress.com/contact/