#RTCNA Wish 2 granted! Create view for every level for every view type

Jason gets another wish granted, this time to “Create a view for every level for every view type in your project file

public void createViewsForViewTypes()
{
    Document doc = this.ActiveUIDocument.Document;
    using (Transaction t = new Transaction(doc, "Create Views"))
    {
        t.Start();
        foreach (ViewFamilyType vft in new FilteredElementCollector(doc)
                 .OfClass(typeof(ViewFamilyType))
                 .Cast<ViewFamilyType>()
                 .Where(q => q.ViewFamily == ViewFamily.FloorPlan))
        {
            foreach (Level level in new FilteredElementCollector(doc)
                     .OfClass(typeof(Level)))
            {
                ViewPlan newview = ViewPlan.Create(doc, vft.Id, level.Id);
                newview.Name = level.Name + "-" + vft.Name;
            }
        }
        t.Commit();
    }
}

#RTCNA Wish 1 granted! Export wall type layer information

Jason wished that the API could be used to “create a data dump of wall types to show the different layers and assigned materials

Two things to consider: Curtain and stacked walls don’t have layers, and <By Category> has no material element ID

public void exportWallTypeStructure()
{
    Document doc = this.ActiveUIDocument.Document;
    
    // set name of output file
    string output = Path.Combine(Path.GetTempPath(), Path.GetFileNameWithoutExtension(doc.PathName) + "-walls.txt");
    
    // create file for writing, overwriting an existing file with the same name if it exists
    using (StreamWriter sw = new StreamWriter(output, false))
    {
        // look at all basic wall types (not stacked & curtain walls)
        foreach (WallType wt in new FilteredElementCollector(doc).OfClass(typeof(WallType)).Cast<WallType>().Where(q => q.Kind == WallKind.Basic))
        {
            sw.Write(wt.Name + ","); // write the name of the wall type
            
            // iterate through every layer in the wall
            foreach (CompoundStructureLayer layer in wt.GetCompoundStructure().GetLayers())
            {
                Element material = doc.GetElement(layer.MaterialId);
                
                // handle case when ID is -1 because layer does not have a material assigned
                string mname = "<By Category>";
                if (material != null)
                    mname = material.Name;
                
                sw.Write(mname + " = " + layer.Width + ",");
            }
            sw.Write(Environment.NewLine);
        }
    }
    Process.Start(output); //open the output file
}

Sample Output:
Generic - 6",<By Category> = 0.5,
Interior - 5" Partition (2-hr),Gypsum Wall Board = 0.0520833333333333,Gypsum Wall Board = 0.0520833333333333,Metal Stud Layer = 0.208333333333333,Gypsum Wall Board = 0.0520833333333333,Gypsum Wall Board = 0.0520833333333333,
Exterior - Brick on Mtl. Stud,Brick, Common = 0.302083333333333,Air = 0.25,Air Infiltration Barrier = 0,Plywood, Sheathing = 0.0625,Metal Stud Layer = 0.5,Vapor Retarder = 0,Gypsum Wall Board = 0.0416666666666667,
Generic - 8",<By Category> = 0.666666666666667,
Exterior - Brick on CMU,Brick, Common = 0.302083333333333,Air = 0.25,Rigid insulation = 0.25,Damp-proofing = 0,Concrete Masonry Units = 0.635416666666667,Metal Furring = 0.135416666666667,Gypsum Wall Board = 0.0520833333333333,
Generic - 12",<By Category> = 1,
Generic - 5",<By Category> = 0.416666666666667,
Generic - 12" Masonry,Concrete Masonry Units = 0.96875,
Generic - 8" Masonry,Concrete Masonry Units = 0.635416666666667,
Generic - 6" Masonry,Concrete Masonry Units = 0.46875,

#RTCEUR Wish 3: Scheduling adaptive point location values

Brian asked how to schedule the location value for placement points in adaptive components.

In a post at https://boostyourbim.wordpress.com/2013/02/14/set-adaptive-component-parameter-values-with-placement-point-coordinates/ I showed how user-defined parameters can be automatically updated with XYZ values of adaptive component points.

If instead what you’d like to do is schedule point parameters such as Measurement Type, Chord Length, and Measure From, the workflow could be something like this:

Capture

 

  1. Create user parameters for each point for each of these parameters
    1. Measurement Type 1
    2. Point Location 1
    3. Measurement Type 2
    4. Point Location 2
    5. etc
  2. Use the API to set these values for every family instance
  3. Use dynamic model update to:
    1. automatically update these user parameters any time the reference point parameters change
    2. automatically update the reference point parameters any time the user parameters change

Capture

This code does step 2 listed above. The approach for Step 3 is covered in the post linked above.
You will note that the Measurement Type is stored internally as an integer and we need to convert from that integer to the corresponding string (Chord Length, Segment Length…) so that we can get the length value from the parameter of the same name.

public void adapt()
{
    Document doc = this.ActiveUIDocument.Document;
    using (Transaction t = new Transaction(doc,"Set Adapt Comp Data"))
    {
        t.Start();
        foreach (FamilyInstance fi in new FilteredElementCollector(doc).OfClass(typeof(FamilyInstance)).Cast<FamilyInstance>())
        {
            int ctr = 1;
            foreach (ElementId pointId in AdaptiveComponentInstanceUtils.GetInstancePlacementPointElementRefIds(fi))
            {
                ReferencePoint referencePoint = doc.GetElement(pointId) as ReferencePoint;
                Parameter measurementTypeParam = referencePoint.get_Parameter("Measurement Type");
                if (measurementTypeParam == null)
                    continue;
                
                int i = measurementTypeParam.AsInteger();
                string s = "";
                double paramValue = 0;
                if (i == 1)
                    s = "Non-Normalized Curve Parameter";
                else if (i == 2)
                    s = "Normalized Curve Parameter";
                else if (i == 3)
                    s = "Segment Length";
                else if (i == 4)
                    s = "Normalized Segment Length";
                else if (i == 5)
                    s = "Chord Length";
                
                paramValue = referencePoint.get_Parameter(s).AsDouble();
                
                fi.get_Parameter("Measurement Type " + ctr).Set(s);
                fi.get_Parameter("Point " + ctr).Set(paramValue);
                
                ctr++;
            }
        }
        t.Commit();
    }
}

#RTCEUR Wish 2: Override element display by View Filters

To conclude this set of 3 posts showing how to override display of elements, this shows how to create a view filter for elements with a Comments instance parameter equal to “override”. The list of element ids is used to set this instance parameter. A view filter is created and set to use this rule, the view filter is applied to the view, and the graphics of this filter is overridden.


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

    OverrideGraphicSettings ogs = new OverrideGraphicSettings();
    ogs.SetCutLineColor(new Color(255,255,0));
    ogs.SetCutLineWeight(6);
    ogs.SetProjectionLineColor(new Color(0,255,255));    
    ogs.SetProjectionLineWeight(6);
    
    IList<ElementId> catIds = new List<ElementId>();
    catIds.Add(doc.Settings.Categories.get_Item("Furniture").Id);
    catIds.Add(doc.Settings.Categories.get_Item("Casework").Id);
    
    IList<FilterRule> rules = new List<FilterRule>();
    ElementId commentParamId = new ElementId(BuiltInParameter.ALL_MODEL_INSTANCE_COMMENTS);
    rules.Add(ParameterFilterRuleFactory.CreateEqualsRule(commentParamId, "override", true));
    using (Transaction t = new Transaction(doc,"View Filter"))
    {
        t.Start();
                        
        foreach (ElementId id in getIds(doc))
        {
            doc.GetElement(id).get_Parameter(BuiltInParameter.ALL_MODEL_INSTANCE_COMMENTS).Set("override");
        }
        
        ParameterFilterElement filter = ParameterFilterElement.Create(doc, "New Filter", catIds, rules);
        doc.ActiveView.AddFilter(filter.Id);
        doc.ActiveView.SetFilterOverrides(filter.Id, ogs);
        t.Commit();
    }
}

#RTCEUR API Wish 2: Override display by element ids (isolate)

@DigDesRev asked “so clash detection gives a report w/ element IDs, can we review the report and on 1 view, override the display settings?”

The simplest way to visualize these elements is with Revit’s ‘Temporary Hide/Isolate’ tool

public void isolateElements()
{
    Document doc = this.ActiveUIDocument.Document;    
    using (Transaction t = new Transaction(doc,"Isolate"))
    {
        t.Start();
        doc.ActiveView.IsolateElementsTemporary(getIds(doc));
        t.Commit();
    }
}

getIds() can be whatever function you want to read a clash detection report from disk, find elements in the Revit model, etc.

#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.

#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 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());
}

#AU2013 Wish granted – Delete topo points in elevation range

Kelly asked “Is it possible to delete points within a range of elevation values?”

Yes!


public void deleteTopoPointsByElevation()
{
    Document doc = this.ActiveUIDocument.Document;
    UIDocument uidoc = new UIDocument(doc);

    double min = 20;
    double max = 30;

    TopographySurface topo = doc.GetElement(uidoc.Selection.PickObject(ObjectType.Element)) as TopographySurface;
    IList<XYZ> pointsToDelete = new List<XYZ>();

    foreach (XYZ pt in topo.GetPoints())
    {
        if (pt.Z > min && pt.Z < max)
            pointsToDelete.Add(pt);
    }

    using (TopographyEditScope tes = new TopographyEditScope(doc, "Topo Edit Scope"))
    {
        tes.Start(topo.Id);

            using (Transaction t = new Transaction(doc, "Update Topo"))
            {
                t.Start();
                topo.DeletePoints(pointsToDelete);
                t.Commit();
            }

        tes.Commit(new myFailuresPreprocessor());
    }
}

public class myFailuresPreprocessor : IFailuresPreprocessor
{
    public FailureProcessingResult PreprocessFailures(FailuresAccessor failuresAccessor)
    {
        return FailureProcessingResult.Continue;
    }
}

#AU2013 Wish granted! Convert multiple dimensions into one

Brok asked “Can you via #Revit API combine individual dims into a single string and remove the old dims?”

Yes!

The tricky part was figuring out how to avoid creating zero-length dimension segments between the references used by adjacent dimensions.

public void dimensionConsolidation()
{
    Document doc = this.ActiveUIDocument.Document;
    Application app = this.Application;
    UIDocument uidoc = new UIDocument(doc);

    IList<ElementId> idsToDelete = new List<ElementId>();
    ReferenceArray dimensionRefsForNewDimension = new ReferenceArray();

    // list of element ids and geometry objects used to weed out duplicate references
    IList<Tuple<ElementId,GeometryObject>> geomObjList = new List<Tuple<ElementId,GeometryObject>>();

    Line line = null;
    DimensionType dimType = null;
    IList<Element> elementList = uidoc.Selection.PickElementsByRectangle();    
    if (elementList.Count == 0)
        return;

    foreach (Element selectedElement in elementList)
    {
        Dimension d = selectedElement as Dimension;
        idsToDelete.Add(d.Id);

        // take the dimension line & dimension type from the first dimension
        if (line == null)
        {
            line = d.Curve as Line;
            dimType = d.DimensionType;
        }

        foreach (Reference dr in d.References)
        {
            Element thisElement = doc.GetElement(dr);
            GeometryObject thisGeomObj = thisElement.GetGeometryObjectFromReference(dr);

            // do not add references to the array if the array already contains a reference
            // to the same geometry element in the same element
            bool duplicate = false;
            foreach (Tuple<ElementId,GeometryObject> myTuple in geomObjList)
            {
                ElementId idInList = myTuple.Item1;
                GeometryObject geomObjInList = myTuple.Item2;
                if (thisElement.Id == idInList && thisGeomObj == geomObjInList)
                {
                    duplicate = true;
                    break;
                }
            }

            if (!duplicate)
            {
                dimensionRefsForNewDimension.Append(dr);
                geomObjList.Add(new Tuple<ElementId, GeometryObject>(thisElement.Id, thisGeomObj));
            }
        }
    }    

    using (Transaction t = new Transaction(doc, "Dimension Consolidation"))
    {
        t.Start();
        Dimension newDim = doc.Create.NewDimension(doc.ActiveView, line, dimensionRefsForNewDimension, dimType);
        doc.Delete(idsToDelete);
        t.Commit();
    }

}

#AU2013 Wish 4 – API-Based Performance Benchmarking

https://twitter.com/PragmaticPrxs says it would be great to have an API-based performance benchmarking tool.

This macro opens every view in the RVT, does some zoom in/zoom out, and iterates through all display styles.  With the Revit 2014 rac_basic_sample_project.rvt, how long does it take to run on your machine?

public void viewPerf()
{
    Document doc = this.ActiveUIDocument.Document;
    Application app = this.Application;
    UIDocument uidoc = new UIDocument(doc);

    Stopwatch sw = new Stopwatch();
    sw.Start();
    int ctr = 0;
    foreach (View v in new FilteredElementCollector(doc)
             .OfClass(typeof(View)).Cast<View>()
             .Where(q => !q.IsTemplate && q.ViewType != ViewType.Internal && q.ViewType != ViewType.ProjectBrowser && q.ViewType != ViewType.Undefined && q.ViewType != ViewType.SystemBrowser && q.ViewType != ViewType.Schedule && q.ViewType != ViewType.ColumnSchedule && q.ViewType != ViewType.PanelSchedule))
    {    
        uidoc.ActiveView = v;                
        uidoc.RefreshActiveView();
        UIView uiview = uidoc.GetOpenUIViews().Cast<UIView>().First(q => q.ViewId == v.Id);
        uidoc.RefreshActiveView();
        uiview.ZoomToFit();
        uidoc.RefreshActiveView();
        uiview.Zoom(2);
        uidoc.RefreshActiveView();
        uiview.Zoom(0.5);
        uidoc.RefreshActiveView();    
        switchDisplayStyles(v, uidoc);

        if (uidoc.GetOpenUIViews().Count > 1)
            uiview.Close();

        ctr++;    
    }
    sw.Stop();
    TaskDialog.Show("Time", sw.Elapsed.ToString());
}

private void switchDisplayStyles(View v, UIDocument uidoc)
{
    if (v.ViewType == ViewType.DraftingView || v.ViewType == ViewType.DrawingSheet || v.ViewType == ViewType.Detail || v.ViewType == ViewType.Legend || v.ViewType == ViewType.Rendering)
        return;

    try
    {
    using (Transaction t = new Transaction(uidoc.Document,"display style"))
    {
        t.Start();
        foreach (DisplayStyle ds in Enum.GetValues(typeof(DisplayStyle)))
        {
            if (ds == DisplayStyle.Undefined || ds == DisplayStyle.Rendering || ds == DisplayStyle.Raytrace)
                continue;

            v.DisplayStyle = ds;
            uidoc.RefreshActiveView();
        }
        t.RollBack();
    }
    }
    catch
    {
        TaskDialog.Show("d",v.ViewType.ToString() + " - " + v.ViewName);
    }
}

#AU2013 Wish #3 – Dockable web browser

Sean asked “Question for you: Is it possible to replace the #Revit Recent Files page with custom HTML, either local file or online?”

There is no straightforward way to replace the Recent Files page (it is built at run-time by Revit). But here are two ways to show a web browser as part of the Revit session.

  1. A modeless, independent dialog with a web browser is showed when Revit starts
  2. A dockable web browser is created after the first Revit file is opened

#AU2013 Wish 2 Granted!

Nancy asked for “auto duplicating and placing views through Design Option set creation”

Here is a macro that creates new views and sheets, with a new view for each design option. Each duplicated view is set to display a different design option and is placed on a sheet in the same location as on the sheet hosting the main model viewport.

public void ViewDuplicateOptions()
{
    Document doc = this.ActiveUIDocument.Document;
    UIDocument uidoc = this.ActiveUIDocument;
    IEnumerable<DesignOption> designOptions = new FilteredElementCollector(doc).OfClass(typeof(DesignOption)).Cast<DesignOption>().Where(q => !q.IsPrimary);
    if (designOptions.Count() == 1)
    {
        TaskDialog.Show("Error","Only one design option exists. No views duplicated.");
        return;
    }

    IList<ViewSheet> newSheets = new List<ViewSheet>();

    IList<ViewSheet> viewSheets = new FilteredElementCollector(doc).OfClass(typeof(ViewSheet)).Cast<ViewSheet>().ToList();

    using (Transaction t = new Transaction(doc,"Duplicate Views by Option"))
    {
        t.Start();
        foreach (ViewSheet vs in viewSheets)
        {
            IEnumerable<Viewport> viewportsOnSheet = new FilteredElementCollector(doc).OfClass(typeof(Viewport)).Cast<Viewport>().Where(q => q.SheetId == vs.Id);
            if (viewportsOnSheet.Count() == 0)
                continue;

            FamilyInstance titleblock = new FilteredElementCollector(doc).OfClass(typeof(FamilyInstance)).OfCategory(BuiltInCategory.OST_TitleBlocks).Cast<FamilyInstance>().First(q => q.OwnerViewId == vs.Id);

            foreach (DesignOption dOpt in designOptions)
            {
                ViewSheet newSheet = ViewSheet.Create(doc, titleblock.GetTypeId());
                newSheet.Name = vs.Name + " - " + dOpt.Name;
                newSheets.Add(newSheet);
                foreach (Viewport vp in viewportsOnSheet)
                {
                    View view = doc.GetElement(vp.ViewId) as View;
                    XYZ vpCenter = vp.GetBoxCenter();
                    View newView = doc.GetElement(view.Duplicate(ViewDuplicateOption.WithDetailing)) as View;
                    newView.Name = view.Name.Replace("{","").Replace("}","") + " - " + dOpt.Name;
                    newView.get_Parameter("Visible In Option").Set(dOpt.Id);
                    Viewport newVp = Viewport.Create(doc, newSheet.Id, newView.Id, vpCenter);
                }
            }
        }
        t.Commit();
    }

    foreach(ViewSheet v in newSheets)
    {
        uidoc.ActiveView = v;
    }
}

#AU2013 Wish 1 Granted!

Matt asked to “duplicate scope box across models”. Open the source file (in this case named “scope boxes.rvt”), then activate the target document and run the macro.

public void CopyScopeBoxBtwDoc()
{
    Document doc = this.ActiveUIDocument.Document;
    Application app = this.Application;

    string filename = "scope boxes.rvt";

    Document sourceDoc = app.Documents.Cast<Document>().FirstOrDefault(q => q.PathName.EndsWith(filename));

    if (sourceDoc == null)
    {
        TaskDialog.Show("Error", "No open document named " + filename);
        return;
    }

    ICollection<ElementId> elementsToCopy = new FilteredElementCollector(sourceDoc).OfCategory(BuiltInCategory.OST_VolumeOfInterest).ToElementIds();

    using (Transaction t = new Transaction(doc,"Copy Scope Box from " + filename))
    {
        t.Start();
        ElementTransformUtils.CopyElements(sourceDoc, elementsToCopy, doc, Transform.Identity, new CopyPasteOptions());
        t.Commit();
    }

}

#RTCEUR Wish 5 – Who Created Those Warnings?

To help BIM Managers train or punish their Revit users, I was asked at RTC in Delft if we can use the API to figure out which users are responsible for each warning in your Revit file.

Here’s how that can be done by writing all warnings to a log file and then comparing warnings in the current file with those in the log.

public void RegisterFailureReporter()
{
    Application app = this.Application;
    Document doc = this.ActiveUIDocument.Document;
    if (doc.PathName == "")
    {
        TaskDialog.Show("Error","Please save the file and then repeat this command.");
        return;
    }
    app.FailuresProcessing += FailureReporter;
}

private void FailureReporter(object sender, Autodesk.Revit.DB.Events.FailuresProcessingEventArgs args)
{
    FailuresAccessor fa = args.GetFailuresAccessor();
    Document doc = fa.GetDocument();

    using (StreamWriter sw = new StreamWriter(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), Path.GetFileName(doc.PathName) + " Warning Creation Log.csv"), true))
    {
        foreach (FailureMessageAccessor fma in fa.GetFailureMessages(FailureSeverity.Warning))
        {
            sw.Write(DateTime.Now + "," + fa.GetDocument().Application.Username + ",");
            sw.Write(fma.GetDescriptionText().Replace(Environment.NewLine,"") + "," );
            foreach (ElementId id in fma.GetFailingElementIds())
            {
                // use UniqueId instead of ElementId because the UniqueId is stable across Save To Central while the ElementId property may change.  
                sw.Write(doc.GetElement(id).UniqueId + ",");
            }
            sw.Write(Environment.NewLine);
        }
    }
}

public void CheckCurrentWarningsInLog()
{
    Document doc = this.ActiveUIDocument.Document;
    if (doc.PathName == "")
    {
        TaskDialog.Show("Error","Please save the file and then repeat this command.");
        return;
    }    
    using (StreamReader warningsHTMLReader = new StreamReader(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), Path.GetFileNameWithoutExtension(doc.PathName) + "_Error Report.html")))
    {
        string line = "";
        while ((line = warningsHTMLReader.ReadLine()) != null)
        {
            if (!line.Contains(": id "))
                continue;

            string[] row = System.Text.RegularExpressions.Regex.Split(line, "id ");

            string id1 = row[1].Split(' ')[0];
            string uniqueId1 = doc.GetElement(new ElementId(int.Parse(id1))).UniqueId;
            string uniqueId2 = null;
            if (row.Count() == 3) // there are two element ids in this row
            {
                string id2 = row[2].Split(' ')[0];
                uniqueId2 = doc.GetElement(new ElementId(int.Parse(id2))).UniqueId;
            }

            using (StreamReader warningLogReader = new StreamReader(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), Path.GetFileName(doc.PathName) + " Warning Creation Log.csv")))
            {
                string lineLog = "";
                while ((lineLog = warningLogReader.ReadLine()) != null)
                {
                    if (lineLog.Contains(uniqueId1) || (uniqueId2 != null && lineLog.Contains(uniqueId1) && lineLog.Contains(uniqueId2)))
                    {
                        using (StreamWriter sw = new StreamWriter(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), Path.GetFileName(doc.PathName) + " Current Warnings With User Info.csv"), true))
                        {
                                   sw.WriteLine(lineLog);
                        }
                        break;
                    }
                }
            }
        }
    }
}

#RTCEUR Wish 4 – Find Native and RVT Link Penetrating Elements

This sample shows how the BoundingBoxIntersectsFilter can be used to find elements that penetrate ceilings in the model.

The host file contains 2 ceilings, 2 columns, and a RVT link that contains 3 Generic Model elements. There are also 3 shaft openings in the ceiling so that there is no interference between the ceilings and the elements going through them.

This API code analyzes the bounding box of each ceiling and finds all elements in all files (including the current RVT and links) whose bounding box intersects the ceiling’s bounding box. The results are shown in a dialog and written to a parameter of the ceiling category.

public void FindPenetrations()
{
    Document hostDoc = this.ActiveUIDocument.Document;
    string data = "";

    LogicalOrFilter columnGenericModelfilter = new LogicalOrFilter(new ElementCategoryFilter(BuiltInCategory.OST_Columns), new ElementCategoryFilter(BuiltInCategory.OST_GenericModel));

    using (Transaction t = new Transaction(hostDoc, "Find Penetrations"))
    {
        t.Start();

        foreach (Ceiling ceiling in new FilteredElementCollector(hostDoc).OfClass(typeof(Ceiling)))
        {
            data += "--- Ceiling - " + ceiling.Name + " (id = " + ceiling.Id + ") ---\n";

            // define an Outline based on the Bounding Box of this ceiling
            Outline outline = new Outline(ceiling.get_BoundingBox(null).Min, ceiling.get_BoundingBox(null).Max);

            string paramData = "";

            // search for penetrations in all open documents which will include RVT Links and native geoemtry in this RVT
            foreach (Document doc in this.Application.Documents)
            {
                // find elements that pass the Column/Generic Model filter and whose Bounding Box intersects the outline of this Ceiling
                foreach (Element e in new FilteredElementCollector(doc).WherePasses(columnGenericModelfilter).WherePasses(new BoundingBoxIntersectsFilter(outline)))
                 {
                    data += e.Category.Name + " - " + e.Name + " - " + Path.GetFileName(e.Document.PathName) + " - " + e.Id.IntegerValue + "\n";
                    paramData += e.Name + " - " + Path.GetFileName(e.Document.PathName) + " - " + e.Id.IntegerValue + "\n";
                 }
            }

            ceiling.get_Parameter("Penetrating Elements").Set(paramData);
            data += "\n";
        }
        t.Commit();    
    }
    TaskDialog.Show("Intersections",data);
}

#RTCEUR Wish 3 – Copy View Types & Templates btw Documents

Q: Would be possible to create a tool that would “Transfer View Types” from one project to another?

Background: View Types have a couple of key properties, an assigned View Template, and if new view instances created from the Type should be dependent on the View Template. But the Revit UI does not allow view types to be transferred between projects.

A: Good news! With the API it is possible to determine the View Types defined in a project, read their associated parameters, create new View Types in a second Project, and set these parameters to match the values in the source project.

public void CopyViewTypes()
{
    Application app = this.Application;

    // source document is the active document
    Document sourceDoc = this.ActiveUIDocument.Document;

    // target document is an open file named "target.rvt"
    Document targetDoc = app.Documents.Cast<Document>().Where(q => Path.GetFileName(q.PathName) == "target.rvt").FirstOrDefault();

    // element ids for all instances of the ViewFamilyType class
    ICollection<ElementId> viewFamilyTypeIds = new FilteredElementCollector(sourceDoc).OfClass(typeof(ViewFamilyType)).ToElementIds();

    // element ids of the view templates used in View Types
    ICollection<ElementId> templateIds = new List<ElementId>();

    // list of pairs of strings - first item in each pair will be the name of the View Type - second item will be the name of the View Template used by that View Type 
    IList<Tuple<string, string>>  ssList = new List<Tuple<string, string>>();

    foreach (ViewFamilyType vft in new FilteredElementCollector(sourceDoc).OfClass(typeof(ViewFamilyType)))
    {
        // element id of the view template used in this View Type
        ElementId defaultTemplateId = vft.get_Parameter(BuiltInParameter.DEFAULT_VIEW_TEMPLATE).AsElementId();

        // go to the next View Type if there is no View Template assigned to this view type
        if (defaultTemplateId == ElementId.InvalidElementId)
            continue;

        templateIds.Add(defaultTemplateId);

        ssList.Add(new Tuple<string, string>(vft.Name, sourceDoc.GetElement(defaultTemplateId).Name));
    }

    CopyPasteOptions options = new CopyPasteOptions();

    using (Transaction t = new Transaction(targetDoc, "Copy View Types"))
    {
        t.Start();
        // copy the view templates
        ElementTransformUtils.CopyElements(sourceDoc, templateIds, targetDoc, Transform.Identity, options);
        // copy the view types
        ElementTransformUtils.CopyElements(sourceDoc, viewFamilyTypeIds, targetDoc, Transform.Identity, options);

        foreach (Tuple<string, string> tup in ssList)
        {
            Element vft = new FilteredElementCollector(targetDoc).OfClass(typeof(ViewFamilyType)).Where(q => q.Name == tup.Item1).FirstOrDefault();

            View template = new FilteredElementCollector(targetDoc).OfClass(typeof(View)).Cast<View>().Where(q => q.IsTemplate && q.Name == tup.Item2).FirstOrDefault();

            // assign the view template to the view type
            vft.get_Parameter(BuiltInParameter.DEFAULT_VIEW_TEMPLATE).Set(template.Id);
        }

        t.Commit();
    }
}

#RTCEUR Wish 2 – Highlight and Isolate Warning Elements

@marcellosgamb suggested an API that runs thru every error in a model turns element w/error red then as u fix each error turn normal

There is no API access to the Revit “Review Warnings” data, so this implementation requires the user to export the warning data. The program then imports this HTML, parses it to find the element IDs, and then isolates and colors the elements in two 3D views.

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

    string filename = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), "warnings.html");

    IList<ElementId> ids = new List<ElementId>();

    using (StreamReader sr = new StreamReader(filename))
    {
        string line = "";
        while ((line = sr.ReadLine()) != null)
        {
            string[] row = System.Text.RegularExpressions.Regex.Split(line,"id ");
            if (row.Length != 3)
            {
                continue;
            }
            string id1 = row[1].Split(' ')[0];
            string id2 = row[2].Split(' ')[0];

            ids.Add(new ElementId(int.Parse(id1)));
            ids.Add(new ElementId(int.Parse(id2)));
        }
    }    

    View isolateView = new FilteredElementCollector(doc).OfClass(typeof(View)).Cast<View>().Where(q => q.Name == "IsolateWarningElements").FirstOrDefault();
    View overrideView = new FilteredElementCollector(doc).OfClass(typeof(View)).Cast<View>().Where(q => q.Name == "OverrideWarningElements").FirstOrDefault();

    OverrideGraphicSettings ogs = new OverrideGraphicSettings();
    Color red = new Color(255, 0, 0);
    ogs.SetProjectionLineColor(red);
    ogs.SetProjectionLineWeight(8);

    Element solidFill = new FilteredElementCollector(doc).OfClass(typeof(FillPatternElement)).Where(q => q.Name.Contains("Solid")).First();

    ogs.SetProjectionFillPatternId(solidFill.Id);
    ogs.SetProjectionFillColor(new Color(0, 255,0));

    using (Transaction t = new Transaction(doc, "Isolate/Override Warnings"))
    {
        t.Start();
        isolateView.IsolateElementsTemporary(ids);

        foreach (ElementId id in ids)
        {
            overrideView.SetElementOverrides(id, ogs);
        }
        t.Commit();
    }
}

#RTCEUR Wish #1 – Push parameter from Host to Hosted

@Jbenoit44 suggested a tool to push a param from any Host (inc. Lev. and ref pl.) to any Hosted & reverse. Doors windows face base fam Parts etc

This implementation drives parameters from Hosts to their Family Instances.

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

    ElementCategoryFilter hostFilter = new ElementCategoryFilter(BuiltInCategory.OST_Walls);
    ElementCategoryFilter hostedFilter = new ElementCategoryFilter(BuiltInCategory.OST_Doors);

    string parameterName = "Rating";

    using (Transaction t = new Transaction(doc, "Set hosted parameters"))
    {
        t.Start();
        foreach (Element host in new FilteredElementCollector(doc).WherePasses(hostFilter))
        {
            Parameter paramHost = host.get_Parameter(parameterName);

            foreach (Element hosted in new FilteredElementCollector(doc)
                     .WherePasses(hostedFilter)
                     .OfClass(typeof(FamilyInstance))
                     .Cast<FamilyInstance>()
                     .Where(q => q.Host.Id == host.Id))
            {
                if (paramHost.StorageType == StorageType.String)
                    hosted.get_Parameter(parameterName).Set(paramHost.AsString());
                else if (paramHost.StorageType == StorageType.Double)
                    hosted.get_Parameter(parameterName).Set(paramHost.AsDouble());
                else if (paramHost.StorageType == StorageType.Integer)
                    hosted.get_Parameter(parameterName).Set(paramHost.AsInteger());
            }
        }
        t.Commit();
    }
}