Detach Beam from Plane

Here is a long-standing question raised once again now by Miroslav Schonauer of Autodesk Consulting and solved with help from Sasha Crotty of the Revit development team:

Question: Is there a way to programmatically replicate the ‘Detach from Plane’ functionality accessible in the user interface through the beam context menu?

I can right click on a structural beam and select ‘Detach from Plane’.
Revit removes the parameter ‘Work plane’, i.e. BuiltInParameter.SKETCH_PLANE_PARAM, and enables the ‘Reference Level’ parameter, which is read-only otherwise.
I would like to replicate this behaviour using the API.

I tried to delete the Work plane parameter but that did not help.
Even the FamilyInstance.Host for a beam is returned as read-only.

Is there a way to achieve this using the API?

Answer: Changing the end elevation of the beam using the end elevation parameters will force it to go out of plane.
That should also detach it from the reference plane.

Response: I need the beam to remain in the same physical position, so should I do it like this, then?

  • First transaction: move the end elevations +dv in plane normal direction.
  • Second transaction: move it back by –dv.

Answer: I think you may be able to get away without two transactions.
Move the beam and then move it back in the same transaction.

If that doesn’t work, you could use a TransactionGroup and assimilate the two transactions into one.

Response: Thanks all a lot.
It works like a charm, all from a single transaction!
Here is some sample code:


  class SelectionFilterBeam : ISelectionFilter
  {
    public bool AllowElement( Element e )
    {
      if( !(e is FamilyInstance) )
      {
        return false;
      }
 
      if( e.Category.Id.IntegerValue != (int)
        BuiltInCategory.OST_StructuralFraming )
      {
        return false;
      }
 
      // In theory could still be a Brace, but 
      // Structural Usage is sometimes NOT set and 
      // cannot be relied upon!
      // So, good enough as "Beam" if here.
 
      return true;
    }
 
    public bool AllowReference( Reference r, XYZ p )
    {
      return true;
    }
  }
 
  [Transaction( TransactionMode.Automatic )]
  public class CmdDetachBeamFromPlane : IExternalCommand
  {
    public Result Execute(
      ExternalCommandData commandData,
      ref string message,
      ElementSet elements )
    {
      UIApplication uiapp = commandData.Application;
      UIDocument uidoc = uiapp.ActiveUIDocument;
      Document doc = uidoc.Document;
 
      // Pick a beam
 
      SelectionFilterBeam selFilterBeam
        = new SelectionFilterBeam();
 
      Reference r = uidoc.Selection.PickObject(
        ObjectType.Element, selFilterBeam,
        "Select a Beam to 'Detach From Plane'" );
 
      FamilyInstance beam = doc.GetElement( r )
        as FamilyInstance;
 
      // Check if it has 'Work Plane' to detach
      //
      // One would expect that it is simplest to 
      // check .Host as commented below, BUT there 
      // are some strange situations where Host IS 
      // null and Revit STILL displays (as read-only):
      // "Work Plane = <not associated>" !?
      //
      //if (null == beam.Host)
      //{
      //  MessageBox.Show("Selected Family Instance of 'Structural Framing' Category has NO 'Work Plane'!");
      //  return Result.Cancelled;
      //}
      //
      // So, must check if that read-only SKETCH_PLANE_PARAM param exists:
 
      if( null == beam.get_Parameter(
        BuiltInParameter.SKETCH_PLANE_PARAM ) )
      {
        MessageBox.Show( "Selected Family Instance "
          + "of 'Structural Framing' Category has NO "
          + "'Work Plane'!" );
 
        return Result.Cancelled;
      }
 
      // In theory, the plane could be non-horizontal 
      // but in 99% should be and in 99.99% for beams 
      // it would NOT be vertical which is the only 
      // case that would not work using the following 
      // (adjusting the elevation):
      // As .Host is RO property, the workaround is 
      // to move the END ELEVATIONs outside the plane
      // which will internally "detach" it in Revit, 
      // then simply move back!
      // Note that Moving the element would not work 
      // as it is constrained to the plane.
 
      double elevOldSta = beam.get_Parameter(
        BuiltInParameter.STRUCTURAL_BEAM_END0_ELEVATION )
          .AsDouble();
 
      double elevOldEnd = beam.get_Parameter(
        BuiltInParameter.STRUCTURAL_BEAM_END1_ELEVATION )
          .AsDouble();
 
      double elevTmpSta = elevOldSta + 1.0;
      double elevTmpEnd = elevOldEnd + 1.0;
 
      // This will "detach from plane"...
 
      beam.get_Parameter(
        BuiltInParameter.STRUCTURAL_BEAM_END0_ELEVATION )
          .Set( elevTmpSta );
 
      beam.get_Parameter(
        BuiltInParameter.STRUCTURAL_BEAM_END1_ELEVATION )
          .Set( elevTmpEnd );
 
      // ...and this move back to the 
      // same original position
 
      beam.get_Parameter(
        BuiltInParameter.STRUCTURAL_BEAM_END0_ELEVATION )
          .Set( elevOldSta );
 
      beam.get_Parameter(
        BuiltInParameter.STRUCTURAL_BEAM_END1_ELEVATION )
          .Set( elevOldEnd );
 
      MessageBox.Show( "Successfully removed the "
        + "'Work Plane' constraint" );
 
      return Result.Succeeded;
    }
  }

Please note the numerous valuable hints included in the code comments.

Also note that a slight performance improvement might be achievable by adjusting just one end of the beam instead of both.

Finally, note that the code could be made more readable by defining shorthand variables for the lengthy built-in parameter enumeration values :-)

Many thanks to Miro and Sascha for this useful solution!


Comments

4 responses to “Detach Beam from Plane”

  1. Jeremy,
    When is your next Revit API training class? I am looking forward to joint your classes.
    Thanks.

  2. Dear Bachir,
    There are none planned right now, I’m afraid. All I can offer right now is the getting started material:
    http://thebuildingcoder.typepad.com/blog/about-the-author.html#2
    It is pretty complete, actually. A training will assume all of that knowledge, and then go on to more specialised topics. These will always depend on the participants anyway.
    Cheers, Jeremy.

  3. Joe Offord Avatar
    Joe Offord

    Hi Jeremy,
    With regards to detaching things from work planes, I often find myself wanting to disassociate (face-based) family instances that have been placed “by face”. The reason being that I want that family instance to remain in its 3d position and not react to any changes to the original face geometry (often caused by reloading/editing of the original host family). In the UI this is pretty cumbersome. Do you see any way to do this in the UI without recreating a cloned family instance?
    Thanks,
    Joe

  4. Dear Joe,
    Please excuse the delay in replying. The reason is simple: I asked the development team several times and had to wait for an answer. It arrived and says:
    I am not aware of an obvious way to do this in the UI, so I suspect there is no obvious way in the API either, just as you hint: “In the UI this is pretty cumbersome.”
    In general, the API seldom supports anything that is not supported by the UI.
    It seems that the stated goal: “I want that family instance to remain in its 3D position and not react to any changes to the original face geometry” contradicts the tenant of being hosted.
    Perhaps you could elaborate a bit on what kind of application this is, and why hosting is both desirable and unwanted. Perhaps the solution is to not have the application use hosted things in the first place.
    I hope this helps.
    Cheers, Jeremy.

Leave a Reply

Discover more from Autodesk Developer Blog

Subscribe now to keep reading and get access to the full archive.

Continue reading