Sustainably Chugging Along

I continue chugging along here, mainly just answering cases.

January and February tend to be peaceful for me after the extreme travelling and conferencing in December.

I will also be reducing my overdue vacation time in the next couple of months, so expect another break or two in the flow.

Here are today's topics I find interesting or have been looking into myself:

Autodesk Corporate Sustainability

Matching the great work done by the Building Performance Analysis team, making strong use of Revit and BIM technologies, Autodesk as a company has committed to reducing its carbon emissions per dollar contribution to GDP by over 9% year over year through 2020, an incredibly long-range goal for a technology company. A review of corporate emissions performance recognized Autodesk and its Sustainability Solutions team as absolute leaders in this area, demonstrating 'tremendous leadership in shaping corporate sustainability metrics to be more grounded in science.'

AU 2013 Classes Available on Demand

What skills could advance your career? Recordings of over 200 classes have been posted online, along with presentations and handouts for the rest of the more 600 classes presented at Autodesk University 2013 in Las Vegas. You can easily get advanced training, explore a new topic, or peer into the future of design software by signing up for a free Autodesk account and starting to learn right away. Here is the AU online overview and a short URL for it.

AllViews Revit SDK Sample Enhancement

Dale Bartlett very kindly provided an enhancement to the AllViews Revit SDK sample. Says he:

The RvtSamples > Views > All Views command retrieves all of the loaded Title Blocks for user selection in the creation of a series of sheets. The code actually builds a collection of Type names using the GetTitleBlocks method, rather than Family:Type names. If each Title Block has a default Type of 'Type 1' (recommended), the following is the result:

List with several Type 1 entries

Selecting one will result in the first found Type (ChooseTitleBlock) rather than the specific Family:Type.

The above test project contains the following Title Block Families:

Title block families defining several Type 1 entries

If a Family has no Types defined, then the default Type name is always listed the same as the Family name, so in most cases the code would work. I guess that is why it hasn’t been picked up previously.

I fixed this by adding a concatenated FamilyName:FamilyTypeName to the list, and searching for the same to return the appropriate FamilySymbol.Id in the following two methods:

Here is the resulting modified and tested code:

  /// <summary>
  /// Retrieve the title block to be generated by its name.
  /// </summary>
  /// <param name="name">The title block name</param>
  public void ChooseTitleBlock( string name )
  {
    if( string.IsNullOrEmpty( name ) )
    {
      throw new ArgumentNullException( "name" );
    }
 
    foreach( FamilySymbol f in m_allTitleBlocks )
    {
      // Original SDK code returns first family 
      // symbol found, e.g. "Type 1" etc.
 
      //if( name.Equals( f.Name ) )
 
      // Replacement by DJB checks for concatenated 
      // family name and type name
 
      if( name.Equals( f.Family.Name
        + ":" + f.Name ) )
      {
        m_titleBlock = f;
        return;
      }
    }
  }
 
  /// <summary>
  /// Retrieve all available title blocks in 
  /// the currently active document.
  /// </summary>
  /// <param name="doc">The currently active document</param>
  private void GetTitleBlocks( Document doc )
  {
    FilteredElementCollector filteredElementCollector
      = new FilteredElementCollector( doc );
 
    filteredElementCollector.OfClass(
      typeof( FamilySymbol ) );
 
    filteredElementCollector.OfCategory(
      BuiltInCategory.OST_TitleBlocks );
 
    m_allTitleBlocks = filteredElementCollector
      .ToElements();
 
    if( 0 == m_allTitleBlocks.Count )
    {
      throw new InvalidOperationException(
        "There is no title block to generate sheet." );
    }
 
    foreach( Element element in m_allTitleBlocks )
    {
      FamilySymbol f = element as FamilySymbol;
 
      // Original SDK code saves only symbol name.
 
      //AllTitleBlocksNames.Add( f.Name );
 
      // Replacement by DJB saves concatenated 
      // family name and type name to ensure 
      // uniqueness in list.
 
      AllTitleBlocksNames.Add( f.Family.Name
        + ":" + f.Name );
 
      if( null == m_titleBlock )
      {
        m_titleBlock = f;
      }
    }
  }

Did I win a t-shirt?

Many thanks for all your help over the year via your Building Coder blog. A lot of people appreciate your efforts I know.

Many thanks to you, Dale, for discovering, fixing and sharing the solution for this issue!

Revit API Methods that Regenerate

A recent ADN case concerns a performance issue creating a large number of roofs, and why each one of them triggers a regeneration of the model:

Question: I have a program that uses a temporary transaction inside a transaction group to make changes to the model. It creates a large number of footprint roofs to represent rooms in a 3D view and shows the user the result in a preview control. However, this is taking a long time in a large model. In the debugger, I can see that each call to Document.Create.NewFootPrintRoof is taking up to 10 seconds. The user interface displays the 'Regenerating' message in the status bar several times before my call returns, so it appears that the model is being regenerated at least once for each call.

How can that be?

I am specifying manual regeneration mode. Revit is not supposed to regenerate the model until a transaction is closed, isn't it? How can I generate all the roofs without triggering a regeneration for each one?

Answer: Sorry, this is working as expected. Unfortunately, setting up the roof properly and ensuring consistency requires an internal regeneration.

This is pointed out explicitly in the Revit API documentation in RevitAPI.chm, which says the following about this method: "This method will regenerate the document even in manual regeneration mode."

There are a number of other Revit API methods that regenerate the model internally as well.

You can find them by searching the help file for the string above, which returns the following:

Revit API calls that regenerate the model

To see yet a couple of entries more, just search for "regenerate manual regeneration mode":

Revit API calls that regenerate the model

Response: In the case above, switching to using floors instead of roofs speeded the process up by a factor of more than ten.

Out of curiosity: is there a reason that roofs regen and floors don’t?

Answer: Sure: a floor is generally flat, whereas a roof with angles can generate an extremely complex shape.

Programmatic Element Cutting

A frequent question is how to cut Revit elements with each other, both in the family and project contexts.

Here is a recent incarnation of the topic that reached me both as an ADN case and a Revit API forum discussion thread on cutting elements:

Question: I'm trying to write a tool that will cut a beam to a reference plane and I'm not sure if it's possible in the API with SolidSolidCutUtils because of the element type that is a beam or reference plane. Here's my code so far:

  TaskDialog.Show( "Step 1",
    "Choose an element as a reference to cut to." );
 
  Reference eRef = m_document.Selection.PickObject(
    ObjectType.Element, "Please reference line." );
 
  TaskDialog.Show( "Step 2", "Choose an element to cut." );
  Reference eCut = m_document.Selection.PickObject(
    ObjectType.Element, "Please pick an element." );
 
  if( eRef != null
    && eRef.ElementId != ElementId.InvalidElementId
    && eCut != null
    && eCut.ElementId != ElementId.InvalidElementId )
  {
    CutElement( eRef, eCut, m_document, doc, activeDoc );
  }
 
  void CutElement( Reference eRef, Reference eCut,
    UIDocument m_document, Document doc,
    Document activeDoc)
  {
    //TaskDialog.Show("Step 2", "Here");
 
    int CutElement = m_document.Document
      .GetElement(eCut).Id.IntegerValue;
    int ReferenceLine = m_document.Document
      .GetElement(eRef).Id.IntegerValue;
 
    Element solidToBeCut = activeDoc.GetElement(
      new ElementId(CutElement));
 
    Element cuttingSolid = activeDoc.GetElement(
      new ElementId(ReferenceLine));
 
    Transaction transaction = new Transaction(activeDoc);
    transaction.Start("AddCutBetweenSolids");
    SolidSolidCutUtils.AddCutBetweenSolids(
      activeDoc, solidToBeCut, cuttingSolid);
    transaction.Commit();
  }

The problem is I get an error at AddCutBetweenSolids that the parameter solidToBeCut isn't of the right type. Is there another method that would accomplish what I'm trying to do or is there a way to fix this method to do what I want?

Answer: There are several different possible ways to perform Boolean operations between Revit elements.

I put together a collection of pointers to various discussions on The Building Coder concerning 3D Booleans and Cutting Elements to help answer your question.

In many cases, within the family environment, the approach you are attempting using SolidSolidCutUtils is one of the best and most effective options.

However, just as you have discovered, it places certain requirements on the input solids.

Just as its name implies, the cutting solid must be a solid.

A reference line definitely does not fulfil this requirement.

You will have to create a solid somehow.

Here is one suggestion of how to achieve this:

Use the reference line you already have and another one perpendicular to it to define a plane, define a large extruded cube based on that plane, and subtract that from your main body.