Rotation of stirrup rebar (rotated beam) API

Rotation of stirrup rebar (rotated beam) API

jaroslav.kopecky
Contributor Contributor
2,202 Views
6 Replies
Message 1 of 7

Rotation of stirrup rebar (rotated beam) API

jaroslav.kopecky
Contributor
Contributor

Hello,

I have issue with stirrups in rotated beams. I described it in structure forum but it should be here.

There is link with description: Rotation of stirrup rebar (rotated beam)

my last post describe how I proceed to create stirrup rebars in general.

 

Can someone point to solution?

Greetings Jaroslav Kopecký.

0 Likes
Accepted solutions (1)
2,203 Views
6 Replies
Replies (6)
Message 2 of 7

Anonymous
Not applicable

Hello,

 

can we get an answer? Otherwise we will not be able to finish the interface for rebars to Revit. I think it would be a pitty if it would not be possible to link RFEM and Revit in that way.

 

I look forward to hearing from you as soon as possible.

 

Best Regards

Walter Rustler

Dlubal Software

0 Likes
Message 3 of 7

jeremytammik
Autodesk
Autodesk

I discussed this with the development team, and they reply:

 

I investigated the issue with the following conclusions:

 

I opened the RVT that was attached in the tread. It generated many debug warnings about non-conformal matrix used in the Rebar element. Probably, this happens because the rebar was created incorrectly, e.g., passing wrong arguments for origin, xVec and YVec.

  

I attached a TestCommand.cs file which achieves the job in two ways:

 

First solution is using the same functions that the customer used: Rebar.CreateFromRebarShape and rebar.ScaleToBox.

 

See  the attached image (first solution.png)  to understand what arguments you should pass:

 

rebar_stirrup_rotation_first_solution.png

  

 

It is very important how you pass origin, xVec and yVec because this data is used to create the bar position transform.

  

It is a transformation from family coordinates (origin (0,0,0), xVec (1,0,0), yVec (0,1,0)) to the new position in the model (origin, xVec and yVec).

  

For the second solution, I used the Rebar.CreateFromCurves function.

 

Here is the complete code:

    

using System;
using System.Collections.Generic;

using Autodesk.Revit.DB;

using Autodesk.Revit.UI;
using Autodesk.Revit.UI.Selection;

using Autodesk.Revit.Attributes;
using Autodesk.Revit.DB.Structure;
using System.Windows.Forms;

namespace TestRebar
{
  [TransactionAttribute(TransactionMode.Manual)]
  public class TestRebar : IExternalCommand
  {
    UIApplication m_uiApp;
    Document m_doc;

    ElementId elementId = ElementId.InvalidElementId;

    public Result Execute(
      ExternalCommandData commandData,
      ref string message,
      ElementSet elements)
    {
      try
      {
        initStuff(commandData);
        if (m_doc == null)
          return Result.Failed;

        Element host = getRebarHost(commandData);
        if (host == null)
        {
          MessageBox.Show("Null host");
          return Result.Succeeded;
        }
          
        RebarShape shape = getRebarShape();
        if (shape == null)
        {
          MessageBox.Show("Null shape");
          return Result.Succeeded;
        }

        RebarBarType rebarType = getRebarBarType();
        if (rebarType == null)
        {
          MessageBox.Show("Null rebarType");
          return Result.Succeeded;
        }

        GeometryElement geometryElement = host.get_Geometry(new Options());
        IList<Curve> edges = getFaceEdges(geometryElement); // this will get the edges in family corrdinates not the global coordinates
        
        Transform trf = Transform.Identity; 
        FamilyInstance famInst = host as FamilyInstance;
        if (famInst != null)
          trf = famInst.GetTransform();

        // SOLUTION 1

        {
          XYZ origin, xAxisDir, yAxisDir, xAxisBox, yAxisBox;
          getOriginXandYvecFromFaceEdges(edges, out origin, out xAxisDir, out yAxisDir, out xAxisBox, out yAxisBox);

          //we obtained origin, xAxis, yAxis in family coordinates. Now we will transform them in global coordinates
          origin = trf.OfPoint(origin);
          xAxisDir = trf.OfVector(xAxisDir);
          yAxisDir = trf.OfVector(yAxisDir);

          xAxisBox = trf.OfVector(xAxisBox);
          yAxisBox = trf.OfVector(yAxisBox);

          using (Transaction tr = new Transaction(m_doc))
          {
            tr.Start("Create Rebar");
            Rebar createdStirrupRebar = Rebar.CreateFromRebarShape(
              m_doc, shape, rebarType, host, origin, xAxisDir, yAxisDir);

            RebarShapeDrivenAccessor rebarStirrupShapeDrivenAccessor
              = createdStirrupRebar.GetShapeDrivenAccessor();
            rebarStirrupShapeDrivenAccessor.SetLayoutAsFixedNumber(
              5, 10, true, true, true);
            rebarStirrupShapeDrivenAccessor.ScaleToBox(
              origin, xAxisBox, yAxisBox);

            tr.Commit();
          }
        }

        // SOLUTION 2

        {
          IList<Curve> rebarSegments = new List<Curve>();

          // transform the edges in global coordinates
          IList<Curve> rebarSegms = new List<Curve>();
          foreach (Curve curve in edges)
            rebarSegms.Add(curve.CreateTransformed(trf));

          // Here you can also offset the curves to respect the cover.

          using (Transaction tr = new Transaction(m_doc))
          {
            tr.Start("Create Rebar from Curves");
            Rebar createdStirrupRebar = Rebar.CreateFromCurves(
              m_doc, RebarStyle.StirrupTie, rebarType, null, null,
              host, -XYZ.BasisX, rebarSegms, RebarHookOrientation.Left,
              RebarHookOrientation.Left, true, false);
            RebarShapeDrivenAccessor rebarStirrupShapeDrivenAccessor
              = createdStirrupRebar.GetShapeDrivenAccessor();
            rebarStirrupShapeDrivenAccessor.SetLayoutAsFixedNumber(
              10, 10, true, true, true);
            tr.Commit();
          }
        }
      }
      catch (Exception e)
      {
        TaskDialog.Show("exception", e.Message);
        return Result.Failed;
      }

      return Result.Succeeded;
    }

    private void getOriginXandYvecFromFaceEdges(
      IList<Curve> edges, out XYZ origin, out XYZ xAxisDir,
      out XYZ yAxisDir, out XYZ xAxisBox, out XYZ yAxisBox)
    {
      origin = new XYZ();
      xAxisDir = new XYZ();
      yAxisDir = new XYZ();
      xAxisBox = new XYZ();
      yAxisBox = new XYZ();

      double minZ = double.MaxValue;

      for (int ii = 0; ii < edges.Count; ii++)
      {
        Line edgeLine = edges[ii] as Line;
        if (edgeLine == null)
          continue;

        int nextii = (ii + 1) % edges.Count;
        Line edgeLineNext = edges[nextii] as Line;
        if (edgeLineNext == null)
          continue;

        XYZ pntEnd = edgeLine.Evaluate(1, true);
        if (pntEnd.Z < minZ)
        {
          minZ = pntEnd.Z;
          origin = pntEnd;
          // These two will be used by Rebar.CreateFromRebarShape.
          // For this the length is irrelevant:
          xAxisDir = edgeLine.Direction * -1;
          yAxisDir = edgeLineNext.Direction;
          // These two  will be used at Rebar.ScaleToBox.
          // For this, the length is important, because it
          // will represent the length of box segment.
          xAxisBox = xAxisDir * edgeLine.ApproximateLength;
          yAxisBox = yAxisDir * edgeLineNext.ApproximateLength; 

          // Here you can also remove from the length the value of the cover.
        }
      }
    }

    private IList<Curve> getFaceEdges(
      GeometryElement geometryElement)
    {
      foreach (GeometryObject geometryObject in geometryElement)
      {
        Solid solid = geometryObject as Solid;
        if (solid != null)
        {
          FaceArray faces = solid.Faces;
          foreach (Face face in faces)
          {
            Plane plane = face.GetSurface() as Plane;
            if (AreEqual(plane.Normal.DotProduct(XYZ.BasisX), -1) ) // vecs are parallel 
            {
              // There should be onlu one curve loop.
              // It can be multiple if the face have a hole.
              if (face.GetEdgesAsCurveLoops().Count != 1)
                return null;

              IList<Curve> edgesArr = new List<Curve>();
              CurveLoopIterator cli = face.GetEdgesAsCurveLoops()[0].GetCurveLoopIterator();
              while (cli.MoveNext())
              {
                Curve edge = cli.Current;
                edgesArr.Add(edge);
              }
              return edgesArr;
            }
          }
        }
        else
        {
          GeometryInstance geometryInstance = geometryObject as GeometryInstance;
          if (geometryInstance != null)
          {
            return getFaceEdges(geometryInstance.GetSymbolGeometry());
          }
        }
      }
      return null; 
    }

    private Element getRebarHost(ExternalCommandData commandData)
    {
      m_uiApp = commandData.Application;
      Selection sel = m_uiApp.ActiveUIDocument.Selection;
      Reference refr = sel.PickObject(ObjectType.Element);
      return m_doc.GetElement(refr.ElementId);
    }

    private RebarBarType getRebarBarType()
    {
      FilteredElementCollector fec = new FilteredElementCollector(m_doc).OfClass(typeof(RebarBarType));
      return fec.FirstElement() as RebarBarType;
      return null;
    }

    private RebarShape getRebarShape()
    {
      FilteredElementCollector fec
        = new FilteredElementCollector(m_doc)
          .OfClass(typeof(RebarShape));
      string strName = "Bügel Geschlossen";
      IList<Element> shapeElems = fec.ToElements();
      foreach (var shapeElem in shapeElems)
      {
        RebarShape shape = shapeElem as RebarShape;
        if (shape.Name.Contains(strName))
          return shape;
      }
      return null;
    }

    public static bool AreEqual(
      double firstValue, double secondValue,
      double tolerance = 1.0e-9)
    {
      return (secondValue - tolerance < firstValue
        && firstValue < secondValue + tolerance);
    }

    void initStuff(ExternalCommandData commandData)
    {
      m_uiApp = commandData.Application;
      m_doc = m_uiApp.ActiveUIDocument.Document;
    }
  }
}

 

I hope this helps.

 

Cheers,

 

Jeremy

 

 



Jeremy Tammik
Developer Technical Services
Autodesk Developer Network, ADN Open
The Building Coder

Message 4 of 7

jeremytammik
Autodesk
Autodesk

The forum answering mechanism is not allowing me to attach the text file to my first answer, so here is a second answer with just the sample command C# source attached... 

 

Nope, I cannot attach the C# source... trying with a zip file instead...

 



Jeremy Tammik
Developer Technical Services
Autodesk Developer Network, ADN Open
The Building Coder

Message 5 of 7

jeremytammik
Autodesk
Autodesk
Accepted solution

Can you please confirm whether my answer helped?

 

Which approach did you choose?

 

What experiences dod you make with it?

 

How did you end up implementing this in your own code?

 

Thank you!

 

Best regards,

 

Jeremy

 



Jeremy Tammik
Developer Technical Services
Autodesk Developer Network, ADN Open
The Building Coder

Message 6 of 7

jaroslav.kopecky
Contributor
Contributor

Hello Jeremy,

Sorry for not to be too quick, I needed experiment and test.

Yes your reply was helpful, Thanks. It has proven that the cause is indeed the vectors.

this is also related (cause/solution) for: missing-zero-length-long-rebars

So first and main conclusion is that the vectors are really very very very sensitive

here is example:

obtained from face as suggested in your example
xAxisDir: (-0,4226182617, 0,9063077870, 0,0000000000)
yAxisDir: (-0,9063077870, -0,4226182617, 0,0000000000)

my previous vectors (only normalized to unit)
xVec unit: (-0,4226177303, 0,9063080349, 0,0000000000)
yVec unit: (-0,9063077319, -0,4226183800, 0,0000000000)

We can see that vectors differ generally on some 6th decimal place. Is this really convenient?

I think it would be more comfortable if your API method would be intelligent or adaptive to correct values for appropriate layout/graphics.
I think, who cares about one-millionth in unit vector.

I retrieve vectors based on intersection with plane in the middle of the beam. Face of the beam could had been modified according to connected beam/column.
(and also comparing them to get to lower left corner) - so this retrieval is a bit complicated..


later I will may be have some more topics related.

e.g. I don't need transform, I already have global coordinates, it is not 100% clear why, may be in time of creation of the beam something is set, and so on.

 

But it could be another post.

 

Thanks, for now,

Jaroslav.

0 Likes
Message 7 of 7

jeremytammik
Autodesk
Autodesk

Thank you for confirming and sorry the API is so fussy. That is normal, I'm afraid, both in Revit and elsewhere...  🙂

 



Jeremy Tammik
Developer Technical Services
Autodesk Developer Network, ADN Open
The Building Coder

0 Likes