Location of Hooks in a Rebar Shape Family

Here is a slightly tricky structural rebar Revit API issue raised by Frode and answered by Tiberiu Pinzariu, Senior Software Engineer at Autodesk, in the Revit API discussion forum thread on getting the location of hooks in a rebar shape family:

Question: When editing a Rebar Shape family I would like to find out what lines in the family the hooks are attached to. In other words I would like the id of the line the start hook is attached to and the coordinate of this hook.

I have written code to edit the family and I do find the rebar lines and the dimensions, but I cannot find any way to locate the placement of the hooks. Any help is appreciated.

Answer: After further investigations I found that this is indeed a tricky subject.

There is no clear/easy way to obtain what the user asked for. (I could not find any API methods for retrieving the position of the hooks in relation to the RebarShape Family lines).

It would be helpful to know more high-level details about what the user wants to obtain.

Could you provide me a scenario?

I am thinking of this one but I need to be sure this is what the user wants.

The user wants to edit the shape family so that all rebars in the model will get a hook at a certain/expected end.

These details would help us decide if need to implement a new method for API, or if we can suggest some other solutions.

Response: What we are trying to do here is to create images of the rebars that will be used in a report. We are able to produce the image shown below, but we would like to be able to put hooks on the image as well. The actual geometry of the hook is not that important. What we need is the hook angle, what end is "Hook at Start" connected to and what is the orientation of the hook.

The code is too long to show here, but what we do is to call EditFamily on the shape. Then we create the rebar shape from the model lines in the family. The dimension lines and text are created from the dimension elements. If there are other ways to create this image we will be very happy. Please note that we need both the hooks and the dimensions in our figure:

Rebar dimensions

Answer: You can get the shape centrelines (without hooks) in order (from start to end) with the following method:

  IList<Curve> curvesForBrowser = rebarShape.GetCurvesForBrowser();

Note: You should be able to retrieve the RebarShape element from the family document.

Hook information can be retrieved through these methods:

  int hookangle0 = rebarShape.GetDefaultHookAngle( 0 );
  RebarHookOrientation orient0 = rebarShape.GetDefaultHookOrientation( 0 );
 
  int hookangle1 = rebarShape.GetDefaultHookAngle( 1 );
  RebarHookOrientation orient1 = rebarShape.GetDefaultHookOrientation( 1 );

Hook position should be as follows:

The connection between the curvesForBrowser and the dimensions in the family can be retrieved like this:

First, map the dimensions to their label parameter ids.

  IDictionary<ElementId, ElementId> labelParamIdToDimId
    = new Dictionary<ElementId, ElementId>();
 
  foreach( Dimension dim in familyDimensions )
  {
    try
    {
      FamilyParameter famParam = dim.FamilyLabel;
      if( null == famParam )
        continue;
      labelParamIdToDimId[famParam.Id] = dim.Id;
    }
    catch( Autodesk.Revit.Exceptions.InvalidOperationException )
    {
      continue;
    }
  }

Secondly, retrieve the constraint information from the shape definition.

  // Get the shape definition : constraints should 
  // be available for every definition.
  // Get all the shape segments (indexes of segments) 
  // and their corresponding constraint parameter ids
  // The RebarShapeSegment should be in the same order 
  // as the curvesForBrowser.
  // The constraint param ids  should correspond to 
  // the dimension label param ids.
 
  RebarShapeDefinitionBySegments defBySeg
    = rebarShape.GetRebarShapeDefinition()
      as RebarShapeDefinitionBySegments;
 
  IDictionary<int, string> segmentPosToLabel
    = new Dictionary<int, string>();
 
  for( int ii = 0; ii < defBySeg.NumberOfSegments; ii++ )
  {
    RebarShapeSegment seg = defBySeg.GetSegment( ii );
 
    IList<RebarShapeConstraint> constraints
      = seg.GetConstraints();
 
    foreach( RebarShapeConstraint constraint
      in constraints )
    {
      ElementId paramID = constraint.GetParamId();
 
      if( ( paramID == ElementId.InvalidElementId )
        || !labelParamIdToDimId.ContainsKey( paramID ) )
      {
        continue;
      }
 
      string labelName = "";
      foreach( Parameter param in rebarShape.Parameters )
      {
        if( param.Id == paramID )
        {
          labelName = param.Definition.Name;
          break;
        }
      }
      segmentPosToLabel.Add( ii, labelName );
    }
  }

Finally, create and retrieve all the curves and their corresponding dimensions:

  IList<Curve> curvesForPicture = new List<Curve>();
  for( int ii = 0; ii < defBySeg.NumberOfSegments; ii++ )
  {
    Curve curve = curvesForBrowser[ii];
    string label = "";
    if( !segmentPosToLabel.TryGetValue( ii, out label ) )
      continue;
  }

Many thanks to Tibi for the in-depth research required to solve this!