Which schedules are on a sheet and which are not?

Jason wonders if there is an easy way to find out which schedules are on a sheet and which are not?

Yes, there is. Find all schedules and then find which ones have a corresponding ScheduleSheetInstance

Capture

public void schedulesSheets()
{
    UIDocument uidoc = this.ActiveUIDocument;
    Document doc = uidoc.Document;   
    IList<string> onSheet = new List<string>();
    IList<string> notOnSheet = new List<string>();
    
    foreach (ViewSchedule vs in new FilteredElementCollector(doc)
             .OfClass(typeof(ViewSchedule))
             .Cast<ViewSchedule>()
             .Where(q => !q.IsTitleblockRevisionSchedule && !q.IsInternalKeynoteSchedule))
    {
        // ScheduleSheetInstance.ScheduleId is the Id of the "master" schedule that generates this ScheduleInstance
        if (new FilteredElementCollector(doc)
            .OfClass(typeof(ScheduleSheetInstance))
            .Cast<ScheduleSheetInstance>()
            .FirstOrDefault(q => q.ScheduleId == vs.Id) == null)
            notOnSheet.Add(vs.Name);
        else
            onSheet.Add(vs.Name);
    }
    
    string data = "--- Schedules On Sheets = " + onSheet.Count() + Environment.NewLine;
    foreach (string s in onSheet)
    {
        data += s + Environment.NewLine;
    }
    data += Environment.NewLine + "--- Schedules Not On Sheets = " + notOnSheet.Count() + Environment.NewLine;
    foreach (string s in notOnSheet)
    {
        data += s + Environment.NewLine;
    }
    TaskDialog.Show("output",data);
}

Adaptive Component Creation

Here is some code that shows how to place adaptive components. In this case, the adaptive component contains 2 placement points. I wrote this to examine the shape of an element’s bounding box – you may find other uses for it.

bboxpublic void bbox()
{
    UIDocument uidoc = this.ActiveUIDocument;
    Document doc = uidoc.Document;   
    
    // select an element
    Element element = doc.GetElement(uidoc.Selection.PickObject(ObjectType.Element));
    
    // get bounding box of the element
    BoundingBoxXYZ bbox = element.get_BoundingBox(null);
    
    // get adaptive component family instance
    FamilySymbol fs = new FilteredElementCollector(doc).OfClass(typeof(FamilySymbol)).Cast<FamilySymbol>().First(q => q.Name == "Type 1" && q.Family.Name == "TwoPoint");
    
    // create instances of the adaptive component
    placeInstance(fs, bbox.Min, bbox.Max, "Space Diagonal");
    placeInstance(fs, bbox.Min, new XYZ(bbox.Max.X, bbox.Max.Y, bbox.Min.Z), "Base diagonal");
    placeInstance(fs, bbox.Min, new XYZ(bbox.Max.X, bbox.Min.Y, bbox.Min.Z), "vx");
    placeInstance(fs, bbox.Min, new XYZ(bbox.Min.X, bbox.Max.Y, bbox.Min.Z), "vy");
    placeInstance(fs, bbox.Min, new XYZ(bbox.Min.X, bbox.Min.Y, bbox.Max.Z), "vz");
}

// create a new instance of the family & set the location of endpoints and Comments parameter
private void placeInstance (FamilySymbol fs, XYZ xyz1, XYZ xyz2, string comment)
{
    Document doc = fs.Document;
    using (Transaction t = new Transaction(doc,"adaptive component"))
    {
        t.Start();
        // place the adaptive component - adaptive points are automatically given some default position during creation
        FamilyInstance fi = AdaptiveComponentInstanceUtils.CreateAdaptiveComponentInstance(doc, fs);
        
        // get references to the adaptive points in the newly created instance
        IList<FamilyPointPlacementReference> refs = fi.GetFamilyPointPlacementReferences();        
        
        // adaptive component points are named "start" and "end"
        // move these points to the input XYZ locations xyz1 and xyz2 
        movePoint(doc, refs.First(q => q.Name == "start"), xyz1);
        movePoint(doc, refs.First(q => q.Name == "end"), xyz2);
        
        // get Comments parameter and set its value
        fi.get_Parameter("Comments").Set(comment);
        t.Commit();
    }
}

// move an FamilyPointPlacementReference to a specified XYZ
private void movePoint(Document doc, FamilyPointPlacementReference point, XYZ xyz)
{
    // need to subtract the default XYZ location of the point from the desired XYZ location
    ElementTransformUtils.MoveElement(doc, point.PointReference.ElementId, xyz.Subtract(locationFromPoint(doc, point)));
}

// get the XYZ location of a FamilyPointPlacementReference
private XYZ locationFromPoint(Document doc, FamilyPointPlacementReference point)
{
    Element e = doc.GetElement(point.PointReference.ElementId);
    ReferencePoint rp = e as ReferencePoint;
    return rp.Position;
}

Download your free trial of the Revit File Logger

The File Logger described last week is now available for download at http://gdurl.com/QrR8/download

Specify the full path to the log file in FileOpenLog.ini in the folder C:\ProgramData\Autodesk\Revit\Addins\<version #>\BoostYourBIM-FileOpenLog
This free trial provides data for all files <50MB. To purchase a full version, contact boostyourbim@gmail.com

Computer name and drive free space have been added, so the complete list of fields collected is:

  1. Open Started Time
  2. Open Completed Time
  3. Elapsed Time (HH:MM:SS)
  4. User Name
  5. File Name
  6. File Size (MB)
  7. Path to Central File
  8. File Last Saved Revit Version
  9. Saved in Current Version (Y/N)
  10. Revit Version Name of current session
  11. Revit Version # of current session
  12. Revit Build # of current session
  13. Computer name
  14. Computer free space on drive containing the Revit file

Sample Data:
Untitled

File Version check enhancements

Thanks for all the positive & constructive feedback on the Version Check tool. Here is a new version with several enhancements:

http://gdurl.com/lbnw/download

  1. Performance enhancements
  2. Cancellable dialog while the tool searches for Revit files
  3. Report includes columns of the following additional data for each file
    1. Size in MB
    2. Last modified date
    3. Last version # (in case you don’t care about the difference between “Revit Architecture 2014″, “Revit MEP 2014″, and “Revit 2014″)
    4. File type (rvt, rfa, rft, rte)
  4. Option to skip backup files
  5. Name of the Excel file contains a date/time stamp so you don’t need to close a previous report before running a new report

Enjoy!

Logging data when opening Revit files

Here’s a prototype of a new tool to track data about your Revit files, who opens them, how long the open takes, if they need to be upgraded, and more. Please leave a comment if you like this or have ideas about how to make it better.

Data Collected:

Open Started Open Completed Elapsed (HH:MM:SS) User File Name File Size (MB) Central File File Last Saved Version Saved in Current Version Revit Version Name Revit Version # Revit Build #

Sample Data:
Untitled Open times are rounded down to the nearest second.

File Version Check – now with the ability to search all subfolders

Thanks to everyone who has mentioned how much they like the File Version Check tool. I’ve added the ability to generate a report on an entire folder & all of its subfolders. The output is also now written to an Excel file instead of a CSV file.

Download it at http://gdurl.com/lbnw/download

Version Check subfolders

With this I was able to run a report on the 13,375 Revit files in C:\ProgramData\Autodesk and generate the report excerpted below.

VersionCheckReport

Random Material generator

The post “Randomize Color and Material in Revit” seemed like a good reason for me to write a post about randomly setting materials.

First, some random number humor:

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

    // get all glass materials in document
    IEnumerable<Element> materials = new FilteredElementCollector(doc)
        .OfClass(typeof(Material))
        .Cast<Material>()
        .Where(q => q.MaterialClass == "Glass");

    using (Transaction t = new Transaction(doc,"Random Materials"))
    {
        t.Start();
        foreach (Element e in new FilteredElementCollector(doc).OfClass(typeof(FamilyInstance)).OfCategory(BuiltInCategory.OST_CurtainWallPanels))
        {
            Parameter materialParam = e.get_Parameter("Material");
            if (materialParam == null)
                continue;

            int randomNumber = new Random(Guid.NewGuid().GetHashCode()).Next(0,materials.Count());

            materialParam.Set(materials.ElementAt(randomNumber).Id);
        }
        t.Commit();
    }
}

Making a beam associated with an edge (part 2)

In the previous post, I showed how to create a beam using the geometry of an edge, and how to store parameter data in the beam that can be used to identify the edge used to create it.

Here is how to update the beams when the edge geometry has changed.

public void UpdateAllBeamsOnEdges()
{
    Document doc = this.ActiveUIDocument.Document;
    UIDocument uidoc = this.ActiveUIDocument;
    SelElementSet selSet = SelElementSet.Create();

    int updatedCtr = 0;
    int skippedCtr = 0;

    using (Transaction t = new Transaction(doc, "Update beams on edges"))
    {
        t.Start();
        // find all beams that have a value for the BeamReference parameter
        // the lambda expression (inside the Where statement) defines a local variable named stableRefParam
        // to help check that both the parameter is not null and that the value of the parameter is not null
        foreach (FamilyInstance fi in new FilteredElementCollector(doc)
                 .OfClass(typeof(FamilyInstance))
                 .OfCategory(BuiltInCategory.OST_StructuralFraming)
                 .Cast<FamilyInstance>()
                 .Where(q => {
                            Parameter stableRefParam = q.get_Parameter("BeamReference"); 
                            return stableRefParam != null &&
                                stableRefParam.AsString() != null;}))
        {
            // get the string from the parameter
            string stableRef = fi.get_Parameter("BeamReference").AsString();

            // get the reference and edge from the stable reference string
            Reference r = Reference.ParseFromStableRepresentation(doc, stableRef);                
            Edge edge = doc.GetElement(r).GetGeometryObjectFromReference(r) as Edge;

            if (edge == null)
            {
                selSet.Add(fi);
                skippedCtr++;
                continue;
            }
            // get the location curve of the beam
            LocationCurve lc = fi.Location as LocationCurve;

            // set the location curve of the beam to the geometry curve of the edge
            lc.Curve = edge.AsCurve();

            updatedCtr++;
        }
        t.Commit();
    }
    uidoc.Selection.Elements = selSet;
    TaskDialog.Show("Info","Updated " + updatedCtr + " beams\n" + "Skipped " + skippedCtr + " highlighted beams (references may no longer exist)");
}

Making a beam associated with an edge (part 1)

Erik asks:

with the UI you can select edges to place a beam but it is not associated to that edge. Can you make it associate?

Yes, we can use the API to solve this problem. In this post I will show how to create the beam and store information about the edge used to create it. In the next post I will show how to update the beam to match the shape of the modified edge.

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

    // find the family symbol for the W-Wide Flange : W12X26 beam
    FamilySymbol beamSymbol = new FilteredElementCollector(doc).OfClass(typeof(FamilySymbol)).Cast<FamilySymbol>()
        .First(q => 
               q.Family.FamilyCategory.Name == "Structural Framing" &&
               q.Family.Name == "W-Wide Flange" && 
               q.Name == "W12X26");

    // find the lowest elevation level in the project (sort levels by elevation, then take the first one)
    Level level = new FilteredElementCollector(doc).OfClass(typeof(Level)).Cast<Level>().OrderBy(q => q.Elevation).First();

    // prompt the user to select an edge
    Reference r = uidoc.Selection.PickObject(ObjectType.Edge,"Pick edge");

    // find the edge from the edge's reference
    Edge edge = doc.GetElement(r).GetGeometryObjectFromReference(r) as Edge;

    using (Transaction t = new Transaction(doc, "Create Beam"))
    {
        t.Start();

        // create a beam on the curve of the edge
        FamilyInstance fi = doc.Create.NewFamilyInstance(edge.AsCurve(), beamSymbol, level, StructuralType.Beam);

        // store a string representation of the edge's reference in a parameter of the beam
        // the value of the parameter will be something like:
        // 99406e0f-efac-4920-9d5f-fe6cea12fc2a-0002cf51:4:LINEAR
        Parameter beamRefParam = fi.get_Parameter("BeamReference");        
        if (beamRefParam == null)
            TaskDialog.Show("Error","Need to create a BeamReference parameter");
        else
            beamRefParam.Set(r.ConvertToStableRepresentation(doc));

        t.Commit();
    }
}

Automating the Building Maker workflow

Tobias suggested that it would be great to see the API used to create Walls by Face, Floors by Face, Roofs by Face, and Mass Floors.

Fortunately, the Revit API has FaceWall.Create and MassInstanceUtils.AddMassLevelDataToMassInstance methods.
Unfortunately, the Revit API has no methods to create Floors by Face and Roofs by Face.

Below is a code sample showing how to create Face Walls on the non-horizontal surfaces and Mass Floors for all levels.

public void CreateFaceWallsAndMassFloors()
{
    Document doc = this.ActiveUIDocument.Document;
    UIDocument uidoc = this.ActiveUIDocument;
    FamilyInstance fi = doc.GetElement(uidoc.Selection.PickObject(ObjectType.Element)) as FamilyInstance;  

    WallType wType = new FilteredElementCollector(doc).OfClass(typeof(WallType)).Cast<WallType>().FirstOrDefault(q => q.Name == "Generic - 6\" Masonry");

    Options opt = new Options();
    opt.ComputeReferences = true;

    using (Transaction t = new Transaction(doc, "Create Face Walls & Mass Floors"))
    {
        t.Start();
        foreach(Solid solid in fi.get_Geometry(opt).Where(q => q is Solid).Cast<Solid>() )
          {
            foreach(Face f in solid.Faces)
              {
                  if (!FaceWall.IsValidFaceReferenceForFaceWall(doc, f.Reference))
                      continue;
                FaceWall.Create( doc, wType.Id, WallLocationLine.CoreExterior, f.Reference );

              }
          }

        foreach (Level level in new FilteredElementCollector(doc).OfClass(typeof(Level)).Cast<Level>())
        {
            MassInstanceUtils.AddMassLevelDataToMassInstance(doc, fi.Id, level.Id);
        }

      t.Commit();
    }
}

Free Revit File Version Tool – Download it today!

Remember this post about Steve’s wish?

When you open a Revit file that was last saved in a previous version, would you love it if Revit asked “Do you want to upgrade?” And would you want a “No” button?

If so, the free File Version Reporter from Boost Your BIM is for you!

  1. Check the ‘last saved version’ for a single file
  2. Check the ‘last saved version’ for all files in a folder
  3. Get a notification dialog when starting to upgrade a file from a previous version with a “No” button

Download the free tool at http://gdurl.com/lbnw/download

The tool is free and also a test of the Pay What You Want approach that Laura Handler mentioned recently. You decide how much time this tool saves you, how much aggravation it helps you avoid, and how much it is worth to you. I hope you will love it and you will make a generous contribution. If you don’t like it, don’t pay anything. If this works for everyone, I’ll happily publish some more Revit API tools this way.