DirectShape Topics and Happy New Year!

Happy New Year!

I had a great break over Christmas and New Year and hope you did as well.

I'll begin the new year by discussing a bunch of topics addressing various aspects of DirectShape elements:

Updated DirectShapeFromFace

One useful little DirectShape utility is
the DirectShapeFromFace add-in.

It creates a DirectShape element from a selected element face.

We initially discussed it in 2015, in the article on DirectShape From Face and Sketch Plane Reuse.

It demonstrates several approaches, including a simpler access to the picked face using GetTotalTransform.

Now I migrated it from Revit 2016 to Revit 2018, replacing a couple of obsolete API calls.

Improve Loft Form Creation Speed using DirectShape

One of the most important features of direct shapes is the ability to generate geometry directly in place in the model without needing to create a separate family definition and instantiate that, resulting in a vast speed improvement.

For example, a developer was creating a new mass family and loading it in the project, taking so much time to create a loft form that it ended up unusable.

The form was generated in a standard way to create families, using Document.FamilyCreate.NewLoftForm with a ReferenceArray of CurveByPoint or ModelCurve elements.

It takes about 4 minutes to create a family in this manner.

Question: Is there any work-around to create a loft form faster?

Answer: Depending on the kind of geometry you want to create, you might be able to use a DirectShape element instead:

Create DirectShape Instead of Conceptual Mass

I the same vein, the Revit API discussion forum thread on creating a surface through Revit API suggests using a DirectShape instead of a conceptual mass element and also introduces the new topic group #5.50 dedicated to the DirectShape element.

Create Surface from Face

Another discussion also deals with creating a surface from a picked element face:

Question: I'm working on a function that creates surfaces from selected faces. It really does not matter what type of element the resulting surface is as long as it gets exported to Navisworks.

What I have been able to do is to collect the faces with the PickObjects method. I have also succeeded creating a mass family representing a surface. This I have achieved by collecting the edges of the face and then I have created the surface object by using NewFormByCap.

But this approach does not work on all faces, especially if one of the edges is a HermiteSpline. I have tried to create a mesh out of the face and then create a tiny surface of each of the triangles using the NewFormByCap method. This do sort of works, but it takes way too long – hours for just a single face.

Do you have any idea how I can solve this issue? It does not have to be a surface; it can also be a very thin solid. The function should work on all possible faces in Revit. Any help is appreciated.

Answer: Surprisingly, yes, I do have a viable suggestion for you.

It is even significantly easier and simpler and more efficient than what you have been trying so far, and I think it can work for all surfaces.

In Revit 2015, the DirectShape element was introduced as a possibility to generate faceted geometry directly in the project space.

All previous methods to define custom geometry required a family definition, like the method you describe.

Here is my simple suggestion:

This discussion ultimately gave rise to the DirectShapeFromFace project discussed above.

Create DirectShape from Solid

Another question on creating direct shapes was raised in the Revit API discussion forum thread on whether Hermite and Nurb Spline need to be created on a plane. It also compares NewLoftForm with DirectShape, and then ends up asking:

Question: You state that you can "... create a DirectShape element using the 'solid' geometry created above." Where can I find code which shows how to do that?

Answer: You just have to package the solid in a GeometryObject array, then DirectShape.CreateElement works, as described in the knowledge base article on DirectShape.

Control DirectShape Colour and Material

Let's round off with some aspects of controlling DirectShape colour and material, e.g., the Revit API discussion forum thread on using API to change color and/or material of DirectShape:

Question: When importing my IFC file, all elements get converted to DirectShape elements.

But they now get colored fully black when viewing in shaded mode, and there seems to be no way to change this.

Only workaround is to overwrite graphics per view, but I would rather work to a more solid solution.

Is there some way to change the color of the DirectShape elements? From what I understood, a material could not be applied to a DirectShape.

When I import more IFC's from the same source with different export settings, it seems I sometimes get different color results.

So, I can conclude that the color is stored somewhere within these DirectShape elements. I still cannot find where, and whether it can be changed though API.

Answer: The best way to control the colour of a direct shape element is probably by setting an appropriate category or subcategory on it.

As you presumably know, there are many ways to control the material and colour of a Revit element

If you prefer setting the colour explicitly, you can look at

Here is some code for that using OverrideGraphicSettings to change the colour of an extrusion object:

  Color color = new Color( 0, 0, 0 ); // RGB (0, 255, 255)
  OverrideGraphicSettings ogs = new OverrideGraphicSettings();
  ogs.SetProjectionLineColor( color );
  ogs.SetProjectionFillColor( color );
  ogs.SetCutFillColor( color );
  ogs.SetCutLineColor( color );
  doc.ActiveView.SetElementOverrides( fiProfile.Id, ogs );

However, controlling the material and colour via the direct shape category is much more BIMish!

If your IFC DirectShapes do not provide Material parameters you can set, and if you cannot change their categories, then you could try to:

The advantage is that you have more control over the DirectShape properties at creation time.

Improve Family Performance Suppressing DirectShape Generation

Question: I use DirectShape to accelerate creating shapes in my family definition.

It includes so many shapes now that Revit exhibits slow performance.

Can I improve this?

Is it possible to temporarily suppress the representation and drawing of DirectShape regeneration?

Answer: Drawing of elements can be suppressed by hiding the associated category in the view, however it is unlikely that the impact of excessive element creation will be limited to creation. Having too many elements in a family will slow down family load and update times. Further I expect that performance problems may be encountered in the project environment as well. There is likely a better, more scalable solution to the problem they are trying to solve that doesn’t involve creation of too many elements.

When you add an element to a family definition, you can always specify its visibility using the FamilyElementVisibility class.

This includes both view direction and level of detail:

This applies to all geometry in a family definition.

Therefore, you can simply define the visibility for all complex direct shapes to be visible only in 3D views and only in fine level of detail.

I would expect that to solve the issue effectively.

Please note that unlike other elements, it is not yet possible to associate DirectShape elements in family definitions with visibility controls. It should apply to all geometric elements, but the operation is actually element based, and has not been enabled for DirectShapes yet. There doesn’t seem to be a Revit Ideas item filed for this functionality, although the team is already aware of the gap.

Happily, this can be worked around making use of the fact that visibility can be set for a family instance nested in the family definition document and creating a FamilyInstance containing the DirectShape inside the family document.

Create DirectShape from Room

Finally, to round off this collectio0n of DirectShape topics, yet another Revit API discussion forum thread on creating direct shape from room and using the spatial geometry calculator versus room boundary to achieve that:

Question: How can I create DirectShape from rooms with holes?

I read many discussion and post but I wasn't able to solve the problem!

More info and source in this GitHub issue:

Last reading:

Many thanks for help!

3D room

Answer: If you want the room with holes, then there will be nothing left.

The room is a hole.

What else is a room except a hole?

Do you mean that you would like to recreate the shape of all the walls with their openings?

Do you mean that you have a room whose plan view includes holes?

Let's assume the latter.

In that case, if your walls are all vertical, this is absolutely trivial.

You can collect the room boundary edges and assemble them into the outer and inner wall loops.

This is demonstrated very effectively by the 2D room editor, which retrieves this information, converts it to a simplified 2D view in SVG, and displays the room outline in a web browser:

Here is a demo recording and lots of other supporting material.

If you want a 3D direct shape instead of a 2D flat version, you will obviously need to determine the ceiling height and location and take that into account as well.

Update:

OK, I now went and read your problem description.

:-)

Very clear. Thank you for that.

Thank you also for your appreciation!

That issue is indeed addressed by the RoomEditorApp that I already pointed to.

When you retrieve the room boundary, the edges may not be contiguous in the right order.

You may need to order them yourself.

There are several ways of achieving that:

DIY. I love do it yourself. Search for SortCurvesContiguous.

ExporterIFCUtils.ValidateCurveLoops. Maybe. The Edge.AsCurveFollowingFace method. Maybe. But read on through the following, and note the note in the last of the discussions listed below.

For the former, here is some more reading material:

I have explored and written lots more on this topic, as you can see if you follow the various links in the discussions listed above.

I am confident that this will give you all you need.

I see that what you are after is basically the floor slabs under the rooms.

I assume that you are not interested in querying the slabs for their geometry?

You want to create these shapes from the room boundaries?

Well, I think you have all you need now for that.

Response: You ask:

"Do you mean that you have a room whose plan view includes holes?"

Yes, I would like to create the volume of the room. If the room has holes (Because walls), I would also like to subtract the volume of the hole.

In short, I feel ... that you translated perfectly my strange English !

"You want to create these shapes from the room boundaries?"

Yes, exactly. For the elevation, I need only a fixed height (very simple).

Update – here is the solution:

It was necessary to add a call in the boundary loop: curveLoopList.Add(curveLoop);

  foreach( IList<BoundarySegment> b in boundaries )
  {
    List<Curve> profile = new List<Curve>();
    ++iBoundary;
    iSegment = 0;

    foreachBoundarySegment s in b )
    {
      ++iSegment;
      Curve curve = s.GetCurve();

      profile.Add( curve );
    }

    try
    {
      CurveLoop curveLoop = CurveLoop.Create( profile );
      curveLoopList.Add( curveLoop );
    }
    catch( Exception ex )
    {
      Debug.WriteLine( ex.Message );
    }
  }

Full code/demo in the Revit3Drooms GitHub repository.

Answer: Maybe I'm missing something here, but why can't you use the SpatialElementGeometryCalculator?

That gives you the solid you require for the DirectShape immediately; I've used this for quite a few implementations.

Does this not take holes into account?

Response: The SpatialElementGeometryCalculator works for rooms?

For a general case this works well but I did not succeed for rooms. Have you already tried ?

Answer: I haven't read through all your material, so I'm not sure about if your specific case needs something special, but it can extract solid geometry and I haven't seen any drawbacks.

The Room type directly inherits SpatialElement, so yes, it sure works.

Here is a sample from the documentation:

  // Calculate a room's geometry and find its boundary faces

  SpatialElementGeometryCalculator calculator 
    = new SpatialElementGeometryCalculator( doc );

  // Compute the room geometry 

  SpatialElementGeometryResults results 
    = calculator.CalculateSpatialElementGeometry( room );

  // Get the solid representing the room geometry

  Solid roomSolid = results.GetGeometry(); 

  foreachFace face in roomSolid.Faces )
  {
    double faceArea = face.Area;

    // Get the sub-faces for the face of the room

    IList<SpatialElementBoundarySubface> subfaceList 
      = results.GetBoundaryFaceInfo( face ); 

    foreachSpatialElementBoundarySubface subface in subfaceList )
    {
      // There are multiple sub-faces that define the face

      if( subfaceList.Count > 1 ) 
      {
        double subfaceArea = subface.GetSubface().Area;
        // get the area of each sub-face
        // sub-faces exist in situations such as 
        // when a room-bounding wall has been
        // horizontally split and the faces of each
        // split wall combine to create the 
        // entire face of the room
      }
    }
  }

Response: Thank you very much! That seems really good!

The only special thing: I must have a cut to 1 ft from the floor.

Answer: The BooleanOperationsUtils class gives you some options where I think the CutWithHalfSpace seems like the most useful one.

However, I mean, if your code works, then go for it. I was just hinting at a more easy solution for future use.

OK, that is more than enough on direct shapes for now, isn't it?