应用程序:MultiplanarRebar

Revit平台:结构

Revit版本:2012.0

首次发布版本:2012.0

编程语言:C#

技能水平:高

类别:结构

类型:ExternalCommand

主题:创建多平面钢筋。

摘要:本示例演示API创建多平面钢筋。多平面钢筋的一个用户场景是悬臂梁的加固。本示例是用于加固倾斜悬臂板。

类:

Autodesk.Revit.UI.IExternalCommand

Autodesk.Revit.Creation.Document

Autodesk.Revit.DB.FamilyInstance

Autodesk.Revit.DB.Structure.Rebar

Autodesk.Revit.DB.Structure.RebarShape

Autodesk.Revit.DB.Structure.RebarShapeDefinition

Autodesk.Revit.DB.Structure.RebarShapeDefinitionBySegments

Autodesk.Revit.DB.Structure.RebarShapeMultiplanarDefinition

Autodesk.Revit.DB.Structure.StructuralType

Autodesk.Revit.DB.Parameter

Autodesk.Revit.DB.DefinitionGroup

Autodesk.Revit.DB.ExternalDefinition

Autodesk.Revit.DB.Solid

项目文件:

Command.cs

这是示例的入口点。它实现了IExternalCommand Execute方法。

 

CorbelFrame.cs

它表示悬臂板的框架,包括梯形剖面和挤压线。可以通过沿着挤压线展开梯形剖面来构建悬臂板。

 

CorbelReinforcementOptions.cs

此类表示悬臂板的加固选项。选项包括通过UI输入从用户收集的钢筋类型和钢筋数量。

 

CorbelReinforcementOptionsForm.cs

这个类是一个表单,用于收集用户对悬臂板钢筋创建的选项。

 

GeometryUtil.cs

此类用于解析给定悬臂板FamilyInstance的几何信息,并根据解析的几何信息最终构造一个CorbelFrame

 

SharedParameterUtil.cs

这是一个实用程序类,用于在Revit文档中创建共享参数。它简化了共享参数创建过程。

描述:

1. 本示例将重点关注悬臂板的加固,悬臂板的主体(墙、柱)的加固超出了此范围,除了需要锚定悬臂板箍筋的直钢筋之外。

2. 悬臂板的钢筋包括水平直钢筋、箍筋和一根多平面钢筋。在悬臂板主体中有两根垂直直钢筋用于锚定悬臂板箍筋。

3. 本示例将提供一个简单的UI来收集钢筋创建选项,如钢筋类型和数量。

详细设计:

1. 第一步是悬臂板几何分析:

a)悬臂板是一个族实例,因此可以通过Element.Geometry()获取其几何形状。本示例需要过滤掉悬臂板的梯形面,因此我们必须获取Solid以进行更深入的解析。

b)遍历Solid的所有面以过滤出梯形面,这是整个示例的关键,多平面钢筋形状和所有钢筋的放置都依赖于这个梯形面。

2. 多平面钢筋形状的创建:

钢筋形状由两个直线段组成,因此我们应该创建RebarShapeDefinitionBySegments,然后根据梯形面添加几何约束。为了创建多平面形状,必须创建一个RebarShapeMultiplanarDefinition。最后,我们调用静态方法RebarShape.Create(…)完成钢筋形状的创建。

3. 在悬臂板和其主体中创建钢筋:

a)使用Rebar.CreateFromCurves创建悬臂板水平直钢筋和悬臂板主体的垂直直钢筋。布置规则设置为固定数量,并使用SetLayoutAsFixedNumber方法。

b)使用形状“T1”通过Rebar.CreateFromRebarShape方法创建箍筋,布置规则使用SetLayoutAsFixedNumber方法设置为固定数量。(三角形区域中填充的箍筋单独放置)。Rebar.ScaleToBox用于将箍筋放置在一个精确计算的边界框中。

c)创建多平面钢筋的第一步是创建多平面形状,然后使用Rebar.CreateFromRebarShape方法来创建钢筋。Rebar.ScaleToBoxFor3D()用于将多平面放置在给定的边界框中。根据梯形面计算边界框。

4.挑战:

这个示例的挑战是对钢筋位置的计算。有几个因素需要考虑:

a)钢筋类型-它包含钢筋直径和弯曲半径的定义。

b)悬臂板和其主体的覆盖距离。

c)多平面钢筋形状的平面弯曲直径。

d)在本示例中,我们考虑了钢筋直径和覆盖距离,但是我们不考虑弯曲半径。换句话说,我们将每个弯曲半径视为零。

说明:

1.设置插件文件并让Revit加载此示例。

2.打开示例文件“Reinforce Corbels.rvt”。

3.选择文档中的悬臂板。支持矩形选择;样本将过滤选择集中的悬臂板。

4.如果选择集包含可以通过此示例加固的悬臂板,则会显示一个窗口并让用户输入简单选项。如果选择不满足条件,则会显示一个警告消息,告知发生了什么。

5.单击窗口上的“确定”按钮以加固悬臂板。

6.Revit UI中检查此示例创建的钢筋。

7.结束。

源代码

完整的源代码请加入QQ群649037449,在群文件中下载RevitSDK.exe,解压后在文件夹中搜索本文中应用程序名称即可获得完整源码

CorbelFrame.cs

//
// (C) Copyright 2003-2019 by Autodesk, Inc.
//
// Permission to use, copy, modify, and distribute this software in
// object code form for any purpose and without fee is hereby granted,
// provided that the above copyright notice appears in all copies and
// that both that copyright notice and the limited warranty and
// restricted rights notice below appear in all supporting
// documentation.
//
// AUTODESK PROVIDES THIS PROGRAM "AS IS" AND WITH ALL FAULTS.
// AUTODESK SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTY OF
// MERCHANTABILITY OR FITNESS FOR A PARTICULAR USE. AUTODESK, INC.
// DOES NOT WARRANT THAT THE OPERATION OF THE PROGRAM WILL BE
// UNINTERRUPTED OR ERROR FREE.
//
// Use, duplication, or disclosure by the U.S. Government is subject to
// restrictions set forth in FAR 52.227-19 (Commercial Computer
// Software - Restricted Rights) and DFAR 252.227-7013(c)(1)(ii)
// (Rights in Technical Data and Computer Software), as applicable.
//

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Autodesk.Revit.DB;
using Autodesk.Revit.DB.Structure;

namespace Revit.SDK.Samples.MultiplanarRebar.CS
{
    /// <summary>
    /// This class represents the trapezoid wire frame profile of corbel.
    /// Its two main functionalities are to create a multi-planar rebar shape and
    /// to calculate the location for rebar creation when reinforcing corbel.  
    /// </summary>
    class Trapezoid
    {
        //
        //             TOP       
        //         |---------\      
        // Vertical|          \Slanted
        //         |   Bottom  \
        //---------|------------\
        //              
        // Top -> Vertical -> Bottom -> Slanted form counter clockwise orientation.

        /// <summary>
        /// Top bound line of this trapezoid.
        /// </summary>
        public Line Top { get; set; }

        /// <summary>
        /// Left vertical bound line of this trapezoid. 
        /// </summary>
        public Line Vertical { get; set; }

        /// <summary>
        /// Bottom bound line of this trapezoid.
        /// </summary>
        public Line Bottom { get; set; }

        /// <summary>
        /// Right slanted bound line of this trapezoid.
        /// </summary>
        public Line Slanted { get; set; }

        /// <summary>
        /// Constructor to initialize the fields.
        /// </summary>
        /// <param name="top">Top Line</param>
        /// <param name="vertical">Left Vertical Line</param>
        /// <param name="bottom">Bottom Line</param>
        /// <param name="slanted">Right slanted Line</param>
        public Trapezoid(Line top, Line vertical, Line bottom, Line slanted)
        {
            Top = top;
            Vertical = vertical;
            Bottom = bottom;
            Slanted = slanted;
        }

        /// <summary>
        /// Draw the trapezoid wire-frame with Revit Model curves.
        /// It's for debug use, to help developer see the exact location.
        /// </summary>
        /// <param name="revitDoc">Revit DB Document</param>
        public void Draw(Document revitDoc)
        {
            XYZ topDir = (Top.GetEndPoint(1) - Top.GetEndPoint(0)).Normalize();
            XYZ verticalDir = (Vertical.GetEndPoint(0) - Vertical.GetEndPoint(1)).Normalize();
            XYZ normal = topDir.CrossProduct(verticalDir);

            SketchPlane sketchplane = SketchPlane.Create(revitDoc, Plane.CreateByNormalAndOrigin(normal, Vertical.GetEndPoint(0)));

            CurveArray curves = new CurveArray();
            curves.Append(Top.Clone());
            curves.Append(Vertical.Clone());
            curves.Append(Bottom.Clone());
            curves.Append(Slanted.Clone());
            revitDoc.Create.NewModelCurveArray(curves, sketchplane);
        }

        /// <summary>
        /// Offset the top line with given value, if the value is positive,
        /// the offset direction is outside, otherwise inside.
        /// </summary>
        /// <param name="offset">Offset value</param>
        public void OffsetTop(double offset)
        {
            XYZ verticalDir = (Vertical.GetEndPoint(0) - Vertical.GetEndPoint(1)).Normalize();
            XYZ verticalDelta = verticalDir * offset;
            XYZ verticalFinal = Vertical.GetEndPoint(0) + verticalDelta;

            double verticalLengthNew = Vertical.Length + offset;
            double slantedLengthNew = verticalLengthNew * Slanted.Length / Vertical.Length;

            XYZ slantedDir = (Slanted.GetEndPoint(1) - Slanted.GetEndPoint(0)).Normalize();
            XYZ slantedFinal = Slanted.GetEndPoint(0) + slantedDir * slantedLengthNew;

            Vertical = Line.CreateBound(verticalFinal, Vertical.GetEndPoint(1));
            Top = Line.CreateBound(slantedFinal, verticalFinal);
            Slanted = Line.CreateBound(Slanted.GetEndPoint(0), slantedFinal);
        }

        /// <summary>
        /// Offset the Left Vertical line with given value, if the value is positive,
        /// the offset direction is outside, otherwise inside.
        /// </summary>
        /// <param name="offset">Offset value</param>
        public void OffsetLeft(double offset)
        {
            XYZ topDir = (Top.GetEndPoint(1) - Top.GetEndPoint(0)).Normalize();
            XYZ topDelta = topDir * offset;

            XYZ topFinal = Top.GetEndPoint(1) + topDelta;
            XYZ bottomFinal = Bottom.GetEndPoint(0) + topDelta;

            Vertical = Line.CreateBound(topFinal, bottomFinal);
            Bottom = Line.CreateBound(bottomFinal, Bottom.GetEndPoint(1));
            Top = Line.CreateBound(Top.GetEndPoint(0), topFinal);
        }

        /// <summary>
        /// Offset the bottom line with given value, if the value is positive,
        /// the offset direction is outside, otherwise inside.
        /// </summary>
        /// <param name="offset">Offset value</param>
        public void OffsetBottom(double offset)
        {
            XYZ verticalDir = (Vertical.GetEndPoint(1) - Vertical.GetEndPoint(0)).Normalize();
            XYZ verticalDelta = verticalDir * offset;
            XYZ verticalFinal = Vertical.GetEndPoint(1) + verticalDelta;

            double verticalLengthNew = Vertical.Length + offset;

            double slantedLengthNew = verticalLengthNew * Slanted.Length / Vertical.Length;

            XYZ slantedDir = (Slanted.GetEndPoint(0) - Slanted.GetEndPoint(1)).Normalize();
            XYZ slantedFinal = Slanted.GetEndPoint(1) + slantedDir * slantedLengthNew;

            Vertical = Line.CreateBound(Vertical.GetEndPoint(0), verticalFinal);
            Bottom = Line.CreateBound(verticalFinal, slantedFinal);
            Slanted = Line.CreateBound(slantedFinal, Slanted.GetEndPoint(1));
        }

        /// <summary>
        /// Offset the right slanted line with given value, if the value is positive,
        /// the offset direction is outside, otherwise inside.
        /// </summary>
        /// <param name="offset">Offset value</param>
        public void OffsetRight(double offset)
        {
            XYZ bottomDir = (Bottom.GetEndPoint(1) - Bottom.GetEndPoint(0)).Normalize();
            XYZ bottomDelta = bottomDir * (offset * Slanted.Length / Vertical.Length);

            XYZ topFinal = Top.GetEndPoint(0) + bottomDelta;
            XYZ bottomFinal = Bottom.GetEndPoint(1) + bottomDelta;

            Top = Line.CreateBound(topFinal, Top.GetEndPoint(1));
            Bottom = Line.CreateBound(Bottom.GetEndPoint(0), bottomFinal);
            Slanted = Line.CreateBound(bottomFinal, topFinal);
        }

        /// <summary>
        /// Deep clone, to avoid mess up the original data during offsetting the boundary.
        /// </summary>
        /// <returns>Cloned object</returns>
        public Trapezoid Clone()
        {
            return new Trapezoid(
                Top.Clone() as Line,
                Vertical.Clone() as Line,
                Bottom.Clone() as Line,
                Slanted.Clone() as Line);
        }

        /// <summary>
        /// Create the multi-planar Rebar Shape according to the trapezoid wire-frame.
        /// </summary>
        /// <param name="revitDoc">Revit DB Document</param>
        /// /// <param name="bendDiameter">OutOfPlaneBendDiameter for multi-planar shape</param>
        /// <returns>Created multi-planar Rebar Shape</returns>
        public RebarShape ConstructMultiplanarRebarShape(Document revitDoc, double bendDiameter)
        {
            // Construct a segment definition with 2 lines.
            RebarShapeDefinitionBySegments shapedef = new RebarShapeDefinitionBySegments(revitDoc, 2);

            // Define parameters for the dimension.
            ElementId B = SharedParameterUtil.GetOrCreateDef("B", revitDoc);
            ElementId H = SharedParameterUtil.GetOrCreateDef("H", revitDoc);
            ElementId K = SharedParameterUtil.GetOrCreateDef("K", revitDoc);
            ElementId MM = SharedParameterUtil.GetOrCreateDef("MM", revitDoc);


            // Set parameters default values according to the size Trapezoid shape. 
            shapedef.AddParameter(B, Top.Length);
            shapedef.AddParameter(H, Bottom.Length - Top.Length);
            shapedef.AddParameter(K, Vertical.Length);
            shapedef.AddParameter(MM, 15);

            // Rebar shape geometry curves consist of Line S0 and Line S1.
            // 
            //
            //         |Y       V1
            //         |--S0(B)--\      
            //         |          \S1(H, K)
            //         |           \
            //---------|O-----------\----X
            //         |       

            // Define Segment 0 (S0)
            //
            // S0's direction is fixed in positive X Axis.
            shapedef.SetSegmentFixedDirection(0, 1, 0);
            // S0's length is determined by parameter B
            shapedef.AddConstraintParallelToSegment(0, B, false, false);

            // Define Segment 1 (S1)
            //
            // Fix S1's direction.
            shapedef.SetSegmentFixedDirection(1, Bottom.Length - Top.Length, -Vertical.Length);
            // S1's length in positive X Axis is parameter H.            
            shapedef.AddConstraintToSegment(1, H, 1, 0, 1, false, false);
            // S1's length in negative Y Axis is parameter K.
            shapedef.AddConstraintToSegment(1, K, 0, -1, 1, false, false);

            // Define Vertex 1 (V1)
            //
            // S1 at V1 is turn to right and the angle is acute.
            shapedef.AddBendDefaultRadius(1, RebarShapeVertexTurn.Right, RebarShapeBendAngle.Acute);

            // Check to see if it's full constrained.  
            if (!shapedef.Complete)
            {
                throw new Exception("Shape was not completed.");
            }

            // Try to solve it to make sure the shape can be resolved with default parameter value.
            if (!shapedef.CheckDefaultParameterValues(0, 0))
            {
                throw new Exception("Can't resolve rebar shape.");
            }

            // Define multi-planar definition
            RebarShapeMultiplanarDefinition multiPlanarDef = new RebarShapeMultiplanarDefinition(bendDiameter);
            multiPlanarDef.DepthParamId = MM;

            // Realize the Rebar shape with creation static method.
            // The RebarStype is stirrupTie, and it will attach to the top cover.
            RebarShape newshape = RebarShape.Create(revitDoc, shapedef, multiPlanarDef,
                RebarStyle.StirrupTie, StirrupTieAttachmentType.InteriorFace,
                0, RebarHookOrientation.Left, 0, RebarHookOrientation.Left, 0);

            // Give a readable name
            newshape.Name = "API Corbel Multi-Shape " + newshape.Id;

            // Make sure we can see the created shape from the browser.
            IList<Curve> curvesForBrowser = newshape.GetCurvesForBrowser();
            if (curvesForBrowser.Count == 0)
            {
                throw new Exception("The Rebar shape is invisible in browser.");
            }

            return newshape;
        }

        /// <summary>
        /// Calculate the boundary coordinate of the wire-frame.
        /// </summary>
        /// <param name="origin">Origin coordinate</param>
        /// <param name="vX">X Vector</param>
        /// <param name="vY">Y Vector</param>
        public void Boundary(out XYZ origin, out XYZ vX, out XYZ vY)
        {
            origin = Vertical.GetEndPoint(1);
            vX = Bottom.GetEndPoint(1) - Bottom.GetEndPoint(0);
            vY = Vertical.GetEndPoint(0) - Vertical.GetEndPoint(1);
        }
    }

    /// <summary>
    /// It represents the frame of Corbel, which is consist of a trapezoid profile and a extrusion line.
    /// Corbel can be constructed by sweeping a trapezoid profile along the extrusion line.
    /// </summary>
    class CorbelFrame
    {
        /// <summary>
        /// Trapezoid profile of corbel family instance.
        /// </summary>
        private Trapezoid m_profile;

        /// <summary>
        /// Extrusion line of corbel family instance.
        /// </summary>
        private Line m_extrusionLine;

        /// <summary>
        /// Corbel family instance.
        /// </summary>
        private FamilyInstance m_corbel;

        /// <summary>
        /// Depth of corbel host.
        /// </summary>
        private double m_hostDepth;

        /// <summary>
        /// Cover distance of corbel family instance.
        /// </summary>
        private double m_corbelCoverDistance;

        /// <summary>
        /// Cover distance of corbel host.
        /// </summary>
        private double m_hostCoverDistance;

        /// <summary>
        /// Constructor to initialize the fields.
        /// </summary>
        /// <param name="corbel">Corbel family instance</param>
        /// <param name="profile">Trapezoid profile</param>
        /// <param name="path">Extrusion Line</param>
        /// <param name="hostDepth">Corbel Host Depth</param>
        /// <param name="hostTopCorverDistance">Corbel Host cover distance</param>
        public CorbelFrame(FamilyInstance corbel, Trapezoid profile,
                Line path, double hostDepth, double hostTopCorverDistance)
        {
            m_profile = profile;
            m_extrusionLine = path;
            m_corbel = corbel;
            m_hostDepth = hostDepth;
            m_hostCoverDistance = hostTopCorverDistance;

            // Get the cover distance of corbel from CommonCoverType.
            RebarHostData rebarHost = RebarHostData.GetRebarHostData(m_corbel);
            m_corbelCoverDistance = rebarHost.GetCommonCoverType().CoverDistance;
        }

        /// <summary>
        /// Parse the geometry of given Corbel and create a CorbelFrame if the corbel is slopped,
        /// otherwise exception thrown.
        /// </summary>
        /// <param name="corbel">Corbel to parse</param>
        /// <returns>A created CorbelFrame</returns>
        public static CorbelFrame Parse(FamilyInstance corbel)
        {
            // This just delegates a call to GeometryUtil class.
            return GeometryUtil.ParseCorbelGeometry(corbel);
        }

        /// <summary>
        /// Add bars to reinforce the Corbel FamilyInstance with given options.
        /// The bars including:
        /// a multi-planar bar, 
        /// top straight bars, 
        /// stirrup bars, 
        /// and host straight bars.
        /// </summary>
        /// <param name="rebarOptions">Options for Rebar Creation</param>
        public void Reinforce(CorbelReinforcementOptions rebarOptions)
        {
            PlaceStraightBars(rebarOptions);

            PlaceMultiplanarRebar(rebarOptions);

            PlaceStirrupBars(rebarOptions);

            PlaceCorbelHostBars(rebarOptions);
        }

        /// <summary>
        /// Add straight bars into corbel with given options. 
        /// </summary>
        /// <param name="options">Options for Rebar Creation</param>
        private void PlaceStraightBars(CorbelReinforcementOptions options)
        {
            Trapezoid profileCopy = m_profile.Clone();
            profileCopy.OffsetTop(-m_corbelCoverDistance);
            profileCopy.OffsetLeft(-m_corbelCoverDistance
                - options.MultiplanarBarType.BarDiameter
                - options.TopBarType.BarDiameter * 0.5);
            profileCopy.OffsetBottom(m_hostDepth - m_hostCoverDistance
                - options.StirrupBarType.BarDiameter
                - options.HostStraightBarType.BarDiameter);
            profileCopy.OffsetRight(-m_corbelCoverDistance);

            //m_profile.Draw(options.RevitDoc);
            //profileCopy.Draw(options.RevitDoc);

            XYZ extruDir = (m_extrusionLine.GetEndPoint(1) - m_extrusionLine.GetEndPoint(0)).Normalize();
            double offset = m_corbelCoverDistance +
                options.StirrupBarType.BarDiameter +
                options.MultiplanarBarType.BarDiameter +
                0.5 * options.TopBarType.BarDiameter;

            Line vetical = profileCopy.Vertical;
            XYZ delta = extruDir * offset;
            Curve barLine = Line.CreateBound(vetical.GetEndPoint(1) + delta, vetical.GetEndPoint(0) + delta);
            IList<Curve> barCurves = new List<Curve>();
            barCurves.Add(barLine);

            Rebar bars = Rebar.CreateFromCurves(options.RevitDoc, RebarStyle.Standard,
                options.TopBarType, null, null, m_corbel, extruDir, barCurves,
                RebarHookOrientation.Left, RebarHookOrientation.Left, true, true);

            bars.GetShapeDrivenAccessor().SetLayoutAsFixedNumber(options.TopBarCount + 2,
                m_extrusionLine.Length - 2 * offset, true, false, false);
            ShowRebar3d(bars);
        }

        /// <summary>
        /// Add a multi-planar bar into corbel with given options.
        /// </summary>
        /// <param name="options">Options for Rebar Creation</param>
        private void PlaceMultiplanarRebar(CorbelReinforcementOptions options)
        {
            Trapezoid profileCopy = m_profile.Clone();
            profileCopy.OffsetTop(-m_corbelCoverDistance
                - options.StirrupBarType.BarDiameter - 0.5 * options.MultiplanarBarType.BarDiameter);
            profileCopy.OffsetLeft(-m_corbelCoverDistance - 0.5 * options.MultiplanarBarType.BarDiameter);
            profileCopy.OffsetBottom(m_hostDepth - m_hostCoverDistance
                - options.HostStraightBarType.BarDiameter * 4
                - options.StirrupBarType.BarDiameter);
            profileCopy.OffsetRight(-m_corbelCoverDistance - options.StirrupBarType.BarDiameter
                - 0.5 * options.StirrupBarType.BarDiameter);

            //m_profile.Draw(options.RevitDoc);
            //profileCopy.Draw(options.RevitDoc);

            XYZ origin, vx, vy;
            profileCopy.Boundary(out origin, out vx, out vy);

            XYZ vecX = vx.Normalize();
            XYZ vecY = vy.Normalize();
            RebarShape barshape = profileCopy.ConstructMultiplanarRebarShape(options.RevitDoc,
                0.5 * options.MultiplanarBarType.StirrupTieBendDiameter);
            Rebar newRebar = Rebar.CreateFromRebarShape(
                options.RevitDoc, barshape,
                options.MultiplanarBarType,
                m_corbel, origin, vecX, vecY);

            XYZ extruDir = (m_extrusionLine.GetEndPoint(1) - m_extrusionLine.GetEndPoint(0)).Normalize();
            double offset = m_corbelCoverDistance +
                options.StirrupBarType.BarDiameter +
                0.5 * options.MultiplanarBarType.BarDiameter;

            newRebar.GetShapeDrivenAccessor().ScaleToBoxFor3D(origin + extruDir * (m_extrusionLine.Length - offset),
                vx, vy, m_extrusionLine.Length - 2 * offset);
            ShowRebar3d(newRebar);
        }

        /// <summary>
        /// Add stirrup bars into corbel with given options.
        /// </summary>
        /// <param name="options">Options for Rebar Creation</param>
        private void PlaceStirrupBars(CorbelReinforcementOptions options)
        {
            var filter = new FilteredElementCollector(options.RevitDoc)
                .OfClass(typeof(RebarShape)).ToElements().Cast<RebarShape>()
                .Where<RebarShape>(shape => shape.RebarStyle == RebarStyle.StirrupTie);

            RebarShape stirrupShape = null;
            foreach (RebarShape shape in filter)
            {
                if (shape.Name.Equals("T1"))
                {
                    stirrupShape = shape; break;
                }
            }

            Trapezoid profileCopy = m_profile.Clone();
            profileCopy.OffsetTop(-m_corbelCoverDistance - 0.5 * options.StirrupBarType.BarDiameter);
            profileCopy.OffsetLeft(-m_corbelCoverDistance - 0.5 * options.StirrupBarType.BarDiameter);
            profileCopy.OffsetBottom(m_hostDepth - m_hostCoverDistance - 0.5 * options.StirrupBarType.BarDiameter);
            profileCopy.OffsetRight(-m_corbelCoverDistance - 0.5 * options.StirrupBarType.BarDiameter);

            XYZ extruDir = (m_extrusionLine.GetEndPoint(1) - m_extrusionLine.GetEndPoint(0)).Normalize();
            double offset = m_corbelCoverDistance + 0.5 * options.StirrupBarType.BarDiameter;

            XYZ origin = profileCopy.Vertical.GetEndPoint(0) + extruDir * offset;
            XYZ xAxis = extruDir;
            XYZ yAxis = (profileCopy.Vertical.GetEndPoint(1) - profileCopy.Vertical.GetEndPoint(0)).Normalize();

            Rebar stirrupBars = Rebar.CreateFromRebarShape(options.RevitDoc, stirrupShape,
                        options.StirrupBarType, m_corbel, origin, xAxis, yAxis);

            double xLength = m_extrusionLine.Length - 2 * offset;
            double yLength = profileCopy.Vertical.Length;

            stirrupBars.GetShapeDrivenAccessor().SetLayoutAsFixedNumber(options.StirrupBarCount + 1, profileCopy.Top.Length, false, false, true);
            stirrupBars.GetShapeDrivenAccessor().ScaleToBox(origin, xAxis * xLength, yAxis * yLength);
            ShowRebar3d(stirrupBars);

            double space = profileCopy.Top.Length / options.StirrupBarCount;
            double step = space * m_profile.Vertical.Length / (m_profile.Bottom.Length - m_profile.Top.Length);

            XYZ dirTop = (m_profile.Top.GetEndPoint(0) - m_profile.Top.GetEndPoint(1)).Normalize();
            XYZ dirVertical = yAxis;
            XYZ deltaStep = dirTop * space + dirVertical * step;

            origin = profileCopy.Top.GetEndPoint(0) + extruDir * offset;
            int count = (int)((m_profile.Vertical.Length - m_corbelCoverDistance - 0.5 * options.StirrupBarType.BarDiameter) / step);
            for (int i = 1; i <= count; i++)
            {
                origin += deltaStep;
                Rebar stirrupBars2 = Rebar.CreateFromRebarShape(options.RevitDoc, stirrupShape,
                    options.StirrupBarType, m_corbel, origin, xAxis, yAxis);

                stirrupBars2.GetShapeDrivenAccessor().ScaleToBox(origin, xAxis * xLength, yAxis * (yLength - i * step));
                ShowRebar3d(stirrupBars2);
            }
        }

        /// <summary>
        /// Add straight bars into corbel Host to anchor corbel stirrup bars.
        /// </summary>
        /// <param name="options">Options for Rebar Creation</param>
        private void PlaceCorbelHostBars(CorbelReinforcementOptions options)
        {
            Trapezoid profileCopy = m_profile.Clone();
            profileCopy.OffsetBottom(m_hostDepth - m_hostCoverDistance
                - options.HostStraightBarType.BarDiameter * 0.5
                - options.StirrupBarType.BarDiameter);

            //profileCopy.Draw(options.RevitDoc);

            XYZ extruDir = (m_extrusionLine.GetEndPoint(1) - m_extrusionLine.GetEndPoint(0)).Normalize();
            double offset = m_corbelCoverDistance + options.StirrupBarType.BarDiameter
                + options.HostStraightBarType.BarDiameter * 0.5;
            XYZ delta = extruDir * offset;

            XYZ pt1 = profileCopy.Bottom.GetEndPoint(0) + delta;
            XYZ pt2 = profileCopy.Bottom.GetEndPoint(1) + delta;

            Curve barLine = Line.CreateBound(pt1, pt2);
            IList<Curve> barCurves = new List<Curve>();
            barCurves.Add(barLine);

            Rebar bars = Rebar.CreateFromCurves(
                options.RevitDoc, RebarStyle.Standard,
                options.HostStraightBarType, null, null, m_corbel.Host, extruDir, barCurves,
                RebarHookOrientation.Left, RebarHookOrientation.Left, true, true);

            bars.GetShapeDrivenAccessor().SetLayoutAsFixedNumber(2, m_extrusionLine.Length - 2 * offset, true, true, true);

            ShowRebar3d(bars);
        }

        /// <summary>
        /// Show the given rebar as solid in 3d view.
        /// </summary>
        /// <param name="rebar">Rebar to show in 3d view as solid</param>
        private void ShowRebar3d(Rebar rebar)
        {
            var filter = new FilteredElementCollector(rebar.Document)
                .OfClass(typeof(View3D));

            foreach (View3D view in filter)
            {
                rebar.IsUnobscuredInView(view);
                rebar.SetSolidInView(view, true);
            }
        }
    }
}

CorbelReinforcementOptions.cs

//
// (C) Copyright 2003-2019 by Autodesk, Inc.
//
// Permission to use, copy, modify, and distribute this software in
// object code form for any purpose and without fee is hereby granted,
// provided that the above copyright notice appears in all copies and
// that both that copyright notice and the limited warranty and
// restricted rights notice below appear in all supporting
// documentation.
//
// AUTODESK PROVIDES THIS PROGRAM "AS IS" AND WITH ALL FAULTS.
// AUTODESK SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTY OF
// MERCHANTABILITY OR FITNESS FOR A PARTICULAR USE. AUTODESK, INC.
// DOES NOT WARRANT THAT THE OPERATION OF THE PROGRAM WILL BE
// UNINTERRUPTED OR ERROR FREE.
//
// Use, duplication, or disclosure by the U.S. Government is subject to
// restrictions set forth in FAR 52.227-19 (Commercial Computer
// Software - Restricted Rights) and DFAR 252.227-7013(c)(1)(ii)
// (Rights in Technical Data and Computer Software), as applicable.
//

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Autodesk.Revit.DB;
using Autodesk.Revit.DB.Structure;

namespace Revit.SDK.Samples.MultiplanarRebar.CS
{
    /// <summary>
    /// Represent the reinforcement options of corbel.
    /// The options include bar type and bar counts which are collected from user via UI input.
    /// </summary>
    class CorbelReinforcementOptions
    {
        /// <summary>
        /// Active Revit DB Document.
        /// </summary>
        public Document RevitDoc { get; set; }

        /// <summary>
        /// List of RebarBarTypes in active document.
        /// </summary>
        public List<RebarBarType> RebarBarTypes { get; set; }

        /// <summary>
        /// RebarBarType for corbel top straight bars.
        /// </summary>
        public RebarBarType TopBarType { get; set; }

        /// <summary>
        /// RebarBarType for corbel stirrup bars.
        /// </summary>
        public RebarBarType StirrupBarType { get; set; }

        /// <summary>
        /// RebarBarType for corbel multi-planar bar.
        /// </summary>
        public RebarBarType MultiplanarBarType { get; set; }

        /// <summary>
        /// RebarBarType for corbel host straight bars.
        /// </summary>
        public RebarBarType HostStraightBarType { get; set; }

        /// <summary>
        /// Count of corbel straight bars.
        /// </summary>
        public int TopBarCount { get; set; }

        /// <summary>
        /// Count of corbel stirrup bars.
        /// </summary>
        public int StirrupBarCount { get; set; }

        /// <summary>
        /// Constructor to initialize the fields.
        /// </summary>
        /// <param name="revitDoc">Revit DB Document</param>
        public CorbelReinforcementOptions(Document revitDoc)
        {
            RevitDoc = revitDoc;
            FilteredElementCollector filteredElementCollector = new FilteredElementCollector(RevitDoc);
            filteredElementCollector.OfClass(typeof(RebarBarType));
            RebarBarTypes = filteredElementCollector.Cast<RebarBarType>().ToList<RebarBarType>();
        }
    }
}

CorbelReinforcementOptionsForm.cs

namespace Revit.SDK.Samples.MultiplanarRebar.CS
{
    partial class CorbelReinforcementOptionsForm
    {
        /// <summary>
        /// Required designer variable.
        /// </summary>
        private System.ComponentModel.IContainer components = null;

        /// <summary>
        /// Clean up any resources being used.
        /// </summary>
        /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
        protected override void Dispose(bool disposing)
        {
            if (disposing && (components != null))
            {
                components.Dispose();
            }
            base.Dispose(disposing);
        }

        #region Windows Form Designer generated code

        /// <summary>
        /// Required method for Designer support - do not modify
        /// the contents of this method with the code editor.
        /// </summary>
        private void InitializeComponent()
        {
            this.okButton = new System.Windows.Forms.Button();
            this.cancelButton = new System.Windows.Forms.Button();
            this.topBarTypeComboBox = new System.Windows.Forms.ComboBox();
            this.stirrupBarTypeComboBox = new System.Windows.Forms.ComboBox();
            this.topBarTypeLabel = new System.Windows.Forms.Label();
            this.stirrupBarTypeLabel = new System.Windows.Forms.Label();
            this.multiplanarBarTypeLabel = new System.Windows.Forms.Label();
            this.multiplanarBarTypeComboBox = new System.Windows.Forms.ComboBox();
            this.topBarCountTextBox = new System.Windows.Forms.TextBox();
            this.stirrupBarCountTextBox = new System.Windows.Forms.TextBox();
            this.topBarGroupBox = new System.Windows.Forms.GroupBox();
            this.topBarCountLabel = new System.Windows.Forms.Label();
            this.stirrupBarGroupBox = new System.Windows.Forms.GroupBox();
            this.stirrupBarCountLabel = new System.Windows.Forms.Label();
            this.multiplanarBarGroupBox = new System.Windows.Forms.GroupBox();
            this.columnGroupBox = new System.Windows.Forms.GroupBox();
            this.columnBarTypeComboBox = new System.Windows.Forms.ComboBox();
            this.columnBarTypeLabel = new System.Windows.Forms.Label();
            this.topBarGroupBox.SuspendLayout();
            this.stirrupBarGroupBox.SuspendLayout();
            this.multiplanarBarGroupBox.SuspendLayout();
            this.columnGroupBox.SuspendLayout();
            this.SuspendLayout();
            // 
            // okButton
            // 
            this.okButton.Location = new System.Drawing.Point(144, 332);
            this.okButton.Name = "okButton";
            this.okButton.Size = new System.Drawing.Size(75, 23);
            this.okButton.TabIndex = 0;
            this.okButton.Text = "OK";
            this.okButton.UseVisualStyleBackColor = true;
            this.okButton.Click += new System.EventHandler(this.okButton_Click);
            // 
            // cancelButton
            // 
            this.cancelButton.DialogResult = System.Windows.Forms.DialogResult.Cancel;
            this.cancelButton.Location = new System.Drawing.Point(269, 332);
            this.cancelButton.Name = "cancelButton";
            this.cancelButton.Size = new System.Drawing.Size(75, 23);
            this.cancelButton.TabIndex = 1;
            this.cancelButton.Text = "Cancel";
            this.cancelButton.UseVisualStyleBackColor = true;
            // 
            // topBarTypeComboBox
            // 
            this.topBarTypeComboBox.FormattingEnabled = true;
            this.topBarTypeComboBox.Location = new System.Drawing.Point(86, 22);
            this.topBarTypeComboBox.Name = "topBarTypeComboBox";
            this.topBarTypeComboBox.Size = new System.Drawing.Size(121, 21);
            this.topBarTypeComboBox.TabIndex = 2;
            this.topBarTypeComboBox.SelectedIndexChanged += new System.EventHandler(this.topBarTypeComboBox_SelectedIndexChanged);
            // 
            // stirrupBarTypeComboBox
            // 
            this.stirrupBarTypeComboBox.FormattingEnabled = true;
            this.stirrupBarTypeComboBox.Location = new System.Drawing.Point(86, 26);
            this.stirrupBarTypeComboBox.Name = "stirrupBarTypeComboBox";
            this.stirrupBarTypeComboBox.Size = new System.Drawing.Size(121, 21);
            this.stirrupBarTypeComboBox.TabIndex = 3;
            this.stirrupBarTypeComboBox.SelectedIndexChanged += new System.EventHandler(this.stirrupBarTypeComboBox_SelectedIndexChanged);
            // 
            // topBarTypeLabel
            // 
            this.topBarTypeLabel.AutoSize = true;
            this.topBarTypeLabel.Location = new System.Drawing.Point(7, 22);
            this.topBarTypeLabel.Name = "topBarTypeLabel";
            this.topBarTypeLabel.Size = new System.Drawing.Size(53, 13);
            this.topBarTypeLabel.TabIndex = 4;
            this.topBarTypeLabel.Text = "Bar Type:";
            // 
            // stirrupBarTypeLabel
            // 
            this.stirrupBarTypeLabel.AutoSize = true;
            this.stirrupBarTypeLabel.Location = new System.Drawing.Point(7, 26);
            this.stirrupBarTypeLabel.Name = "stirrupBarTypeLabel";
            this.stirrupBarTypeLabel.Size = new System.Drawing.Size(53, 13);
            this.stirrupBarTypeLabel.TabIndex = 5;
            this.stirrupBarTypeLabel.Text = "Bar Type:";
            // 
            // multiplanarBarTypeLabel
            // 
            this.multiplanarBarTypeLabel.AutoSize = true;
            this.multiplanarBarTypeLabel.Location = new System.Drawing.Point(7, 25);
            this.multiplanarBarTypeLabel.Name = "multiplanarBarTypeLabel";
            this.multiplanarBarTypeLabel.Size = new System.Drawing.Size(53, 13);
            this.multiplanarBarTypeLabel.TabIndex = 6;
            this.multiplanarBarTypeLabel.Text = "Bar Type:";
            // 
            // multiplanarBarTypeComboBox
            // 
            this.multiplanarBarTypeComboBox.FormattingEnabled = true;
            this.multiplanarBarTypeComboBox.Location = new System.Drawing.Point(86, 22);
            this.multiplanarBarTypeComboBox.Name = "multiplanarBarTypeComboBox";
            this.multiplanarBarTypeComboBox.Size = new System.Drawing.Size(121, 21);
            this.multiplanarBarTypeComboBox.TabIndex = 7;
            this.multiplanarBarTypeComboBox.SelectedIndexChanged += new System.EventHandler(this.multiplanarBarTypeComboBox_SelectedIndexChanged);
            // 
            // topBarCountTextBox
            // 
            this.topBarCountTextBox.Location = new System.Drawing.Point(334, 22);
            this.topBarCountTextBox.Name = "topBarCountTextBox";
            this.topBarCountTextBox.Size = new System.Drawing.Size(100, 20);
            this.topBarCountTextBox.TabIndex = 8;
            this.topBarCountTextBox.Validating += new System.ComponentModel.CancelEventHandler(this.topBarCountTextBox_Validating);
            // 
            // stirrupBarCountTextBox
            // 
            this.stirrupBarCountTextBox.Location = new System.Drawing.Point(334, 26);
            this.stirrupBarCountTextBox.Name = "stirrupBarCountTextBox";
            this.stirrupBarCountTextBox.Size = new System.Drawing.Size(100, 20);
            this.stirrupBarCountTextBox.TabIndex = 9;
            this.stirrupBarCountTextBox.Validating += new System.ComponentModel.CancelEventHandler(this.stirrupBarCountTextBox_Validating);
            // 
            // topBarGroupBox
            // 
            this.topBarGroupBox.Controls.Add(this.topBarCountLabel);
            this.topBarGroupBox.Controls.Add(this.topBarTypeComboBox);
            this.topBarGroupBox.Controls.Add(this.topBarTypeLabel);
            this.topBarGroupBox.Controls.Add(this.topBarCountTextBox);
            this.topBarGroupBox.Location = new System.Drawing.Point(12, 80);
            this.topBarGroupBox.Name = "topBarGroupBox";
            this.topBarGroupBox.Size = new System.Drawing.Size(448, 54);
            this.topBarGroupBox.TabIndex = 10;
            this.topBarGroupBox.TabStop = false;
            this.topBarGroupBox.Text = "Top Bars";
            // 
            // topBarCountLabel
            // 
            this.topBarCountLabel.AutoSize = true;
            this.topBarCountLabel.Location = new System.Drawing.Point(254, 22);
            this.topBarCountLabel.Name = "topBarCountLabel";
            this.topBarCountLabel.Size = new System.Drawing.Size(57, 13);
            this.topBarCountLabel.TabIndex = 9;
            this.topBarCountLabel.Text = "Bar Count:";
            // 
            // stirrupBarGroupBox
            // 
            this.stirrupBarGroupBox.Controls.Add(this.stirrupBarCountLabel);
            this.stirrupBarGroupBox.Controls.Add(this.stirrupBarTypeComboBox);
            this.stirrupBarGroupBox.Controls.Add(this.stirrupBarTypeLabel);
            this.stirrupBarGroupBox.Controls.Add(this.stirrupBarCountTextBox);
            this.stirrupBarGroupBox.Location = new System.Drawing.Point(12, 164);
            this.stirrupBarGroupBox.Name = "stirrupBarGroupBox";
            this.stirrupBarGroupBox.Size = new System.Drawing.Size(448, 54);
            this.stirrupBarGroupBox.TabIndex = 11;
            this.stirrupBarGroupBox.TabStop = false;
            this.stirrupBarGroupBox.Text = "Stirrup Bars";
            // 
            // stirrupBarCountLabel
            // 
            this.stirrupBarCountLabel.AutoSize = true;
            this.stirrupBarCountLabel.Location = new System.Drawing.Point(254, 26);
            this.stirrupBarCountLabel.Name = "stirrupBarCountLabel";
            this.stirrupBarCountLabel.Size = new System.Drawing.Size(57, 13);
            this.stirrupBarCountLabel.TabIndex = 10;
            this.stirrupBarCountLabel.Text = "Bar Count:";
            // 
            // multiplanarBarGroupBox
            // 
            this.multiplanarBarGroupBox.Controls.Add(this.multiplanarBarTypeComboBox);
            this.multiplanarBarGroupBox.Controls.Add(this.multiplanarBarTypeLabel);
            this.multiplanarBarGroupBox.Location = new System.Drawing.Point(12, 251);
            this.multiplanarBarGroupBox.Name = "multiplanarBarGroupBox";
            this.multiplanarBarGroupBox.Size = new System.Drawing.Size(448, 54);
            this.multiplanarBarGroupBox.TabIndex = 12;
            this.multiplanarBarGroupBox.TabStop = false;
            this.multiplanarBarGroupBox.Text = "Multiplanar Bars";
            // 
            // columnGroupBox
            // 
            this.columnGroupBox.Controls.Add(this.columnBarTypeComboBox);
            this.columnGroupBox.Controls.Add(this.columnBarTypeLabel);
            this.columnGroupBox.Location = new System.Drawing.Point(12, 12);
            this.columnGroupBox.Name = "columnGroupBox";
            this.columnGroupBox.Size = new System.Drawing.Size(448, 54);
            this.columnGroupBox.TabIndex = 13;
            this.columnGroupBox.TabStop = false;
            this.columnGroupBox.Text = "Host Straight Bars";
            // 
            // columnBarTypeComboBox
            // 
            this.columnBarTypeComboBox.FormattingEnabled = true;
            this.columnBarTypeComboBox.Location = new System.Drawing.Point(86, 22);
            this.columnBarTypeComboBox.Name = "columnBarTypeComboBox";
            this.columnBarTypeComboBox.Size = new System.Drawing.Size(121, 21);
            this.columnBarTypeComboBox.TabIndex = 7;
            this.columnBarTypeComboBox.SelectedIndexChanged += new System.EventHandler(this.columnBarTypeComboBox_SelectedIndexChanged);
            // 
            // columnBarTypeLabel
            // 
            this.columnBarTypeLabel.AutoSize = true;
            this.columnBarTypeLabel.Location = new System.Drawing.Point(7, 25);
            this.columnBarTypeLabel.Name = "columnBarTypeLabel";
            this.columnBarTypeLabel.Size = new System.Drawing.Size(53, 13);
            this.columnBarTypeLabel.TabIndex = 6;
            this.columnBarTypeLabel.Text = "Bar Type:";
            // 
            // CorbelReinforcementOptionsForm
            // 
            this.AcceptButton = this.okButton;
            this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
            this.BackColor = System.Drawing.SystemColors.Control;
            this.CancelButton = this.cancelButton;
            this.ClientSize = new System.Drawing.Size(476, 387);
            this.ControlBox = false;
            this.Controls.Add(this.columnGroupBox);
            this.Controls.Add(this.multiplanarBarGroupBox);
            this.Controls.Add(this.stirrupBarGroupBox);
            this.Controls.Add(this.topBarGroupBox);
            this.Controls.Add(this.cancelButton);
            this.Controls.Add(this.okButton);
            this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog;
            this.MinimizeBox = false;
            this.Name = "CorbelReinforcementOptionsForm";
            this.ShowIcon = false;
            this.ShowInTaskbar = false;
            this.Text = "Corbel Reinforcement Options";
            this.topBarGroupBox.ResumeLayout(false);
            this.topBarGroupBox.PerformLayout();
            this.stirrupBarGroupBox.ResumeLayout(false);
            this.stirrupBarGroupBox.PerformLayout();
            this.multiplanarBarGroupBox.ResumeLayout(false);
            this.multiplanarBarGroupBox.PerformLayout();
            this.columnGroupBox.ResumeLayout(false);
            this.columnGroupBox.PerformLayout();
            this.ResumeLayout(false);

        }

        #endregion

        private System.Windows.Forms.Button okButton;
        private System.Windows.Forms.Button cancelButton;
        private System.Windows.Forms.ComboBox topBarTypeComboBox;
        private System.Windows.Forms.ComboBox stirrupBarTypeComboBox;
        private System.Windows.Forms.Label topBarTypeLabel;
        private System.Windows.Forms.Label stirrupBarTypeLabel;
        private System.Windows.Forms.Label multiplanarBarTypeLabel;
        private System.Windows.Forms.ComboBox multiplanarBarTypeComboBox;
        private System.Windows.Forms.TextBox topBarCountTextBox;
        private System.Windows.Forms.TextBox stirrupBarCountTextBox;
        private System.Windows.Forms.GroupBox topBarGroupBox;
        private System.Windows.Forms.GroupBox stirrupBarGroupBox;
        private System.Windows.Forms.GroupBox multiplanarBarGroupBox;
        private System.Windows.Forms.Label topBarCountLabel;
        private System.Windows.Forms.Label stirrupBarCountLabel;
        private System.Windows.Forms.GroupBox columnGroupBox;
        private System.Windows.Forms.ComboBox columnBarTypeComboBox;
        private System.Windows.Forms.Label columnBarTypeLabel;

    }
}

GeometryUtil.cs

//
// (C) Copyright 2003-2019 by Autodesk, Inc.
//
// Permission to use, copy, modify, and distribute this software in
// object code form for any purpose and without fee is hereby granted,
// provided that the above copyright notice appears in all copies and
// that both that copyright notice and the limited warranty and
// restricted rights notice below appear in all supporting
// documentation.
//
// AUTODESK PROVIDES THIS PROGRAM "AS IS" AND WITH ALL FAULTS.
// AUTODESK SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTY OF
// MERCHANTABILITY OR FITNESS FOR A PARTICULAR USE. AUTODESK, INC.
// DOES NOT WARRANT THAT THE OPERATION OF THE PROGRAM WILL BE
// UNINTERRUPTED OR ERROR FREE.
//
// Use, duplication, or disclosure by the U.S. Government is subject to
// restrictions set forth in FAR 52.227-19 (Commercial Computer
// Software - Restricted Rights) and DFAR 252.227-7013(c)(1)(ii)
// (Rights in Technical Data and Computer Software), as applicable.
//

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Autodesk.Revit.DB;
using Autodesk.Revit.DB.Structure;

namespace Revit.SDK.Samples.MultiplanarRebar.CS
{
   /// <summary>
   /// This class is to parse the geometry information of given Corbel FamilyInstance,
   /// and finally construct a CorbelFrame according to the parsed geometry information.
   /// </summary>
   class GeometryUtil
   {
      /// <summary>
      /// This method parses geometry information of given Corbel to construct the CorbelFrame.
      /// </summary>
      /// <param name="corbel">Given corbel family instance to parse</param>
      /// <returns>CorbelFrame object</returns>
      public static CorbelFrame ParseCorbelGeometry(FamilyInstance corbel)
      {
         // Get Corbel Host information.
         Element corbelHost = corbel.Host;
         Reference corbelHostFace = corbel.HostFace;

         PlanarFace hostPlane = corbelHost.GetGeometryObjectFromReference(corbelHostFace) as PlanarFace;
         XYZ hostNormal = GetNormalOutside(hostPlane);

         // Extract the faces in Corbel parallel with Corbel host face.
         Solid corbelSolid = GetElementSolid(corbel);
         PlanarFace corbelTopFace = null;
         PlanarFace corbelBottomFace = null;
         foreach (Face face in corbelSolid.Faces)
         {
            PlanarFace planarFace = face as PlanarFace;
            XYZ normal = GetNormalOutside(planarFace);
            if (normal.IsAlmostEqualTo(hostNormal))
            {
               corbelTopFace = planarFace;
            }
            else if (normal.IsAlmostEqualTo(-hostNormal))
            {
               corbelBottomFace = planarFace;
            }
         }

         // Extract the faces in Corbel Host parallel with Corbel host face.
         Solid hostSolid = GetElementSolid(corbelHost);
         PlanarFace hostTopFace = null;
         PlanarFace hostBottomFace = hostPlane;
         foreach (Face face in hostSolid.Faces)
         {
            PlanarFace planarFace = face as PlanarFace;
            XYZ normal = GetNormalOutside(planarFace);
            if (normal.IsAlmostEqualTo(-hostNormal))
            {
               hostTopFace = planarFace;
            }
         }

         // Parse the side faces to find out the Trapezoid face.
         Edge topEdge = null;
         Edge leftEdge = null;
         Edge bottomEdge = null;
         Edge rightEdge = null;
         PlanarFace trapezoidFace = null;
         int foundEdgeIndex = -1;
         bool foundTrapezoid = false;
         EdgeArray bottomEdges = corbelBottomFace.EdgeLoops.get_Item(0);
         foreach (Edge edge in bottomEdges)
         {
            bottomEdge = edge;
            foundEdgeIndex++;
            foundTrapezoid = IsTrapezoid(hostNormal, corbelBottomFace, bottomEdge,
                out trapezoidFace, out topEdge, out leftEdge, out rightEdge);
            if (foundTrapezoid)
            {
               break;
            }
         }

         // Check to see if the Trapezoid faces was found.
         if (!foundTrapezoid)
         {
            // Throw if no any trapezoid face in corbel.
            throw new Exception("Didn't find the trapezoid face in corbel [Id:" + corbel.Id + "].");
         }

         Edge depthEdge = bottomEdges.get_Item((foundEdgeIndex + 1) % bottomEdges.Size);

         double hostDepth = GetDistance(hostTopFace, hostBottomFace);

         // Compute the host face cover distance.
         RebarHostData corbelHostData = RebarHostData.GetRebarHostData(corbelHost);
         // Get CoverType of the given host face
         RebarCoverType coverType = corbelHostData.GetCoverType(hostTopFace.Reference);
         // if the host face don't have a CoverType, then try to get the common CoverType.
         if (coverType == null)
            coverType = corbelHostData.GetCommonCoverType();
         // Get the Cover Distance
         double coverDistance = coverType.CoverDistance;

         // Construct the CorbelFrame from the given parsed trapezoid information.
         return ConstructCorbelFrame(
             corbel, depthEdge,
             leftEdge, bottomEdge, rightEdge, topEdge,
             corbel.Document, trapezoidFace,
             hostDepth, coverDistance);
      }

      /// <summary>
      /// Check if the given bottom edge was shared by a trapezoid face with left edge vertical.
      /// </summary>
      /// <param name="hostNormal">Corbel Host face Normal</param>
      /// <param name="corbelBottomFace">Bottom Face of Corbel</param>
      /// <param name="bottomEdge">Given bottom edge to test</param>
      /// <param name="trapezoidFace">Output the trapezoid Face</param>
      /// <param name="topEdge">Output trapezoid top edge</param>
      /// <param name="leftEdge">Output trapezoid left edge</param>
      /// <param name="rightEdge">Output trapezoid right edge</param>
      /// <returns>True if there is a trapezoid face share the given bottom edge, otherwise false.</returns>
      private static bool IsTrapezoid(
          XYZ hostNormal, PlanarFace corbelBottomFace, Edge bottomEdge,
          out PlanarFace trapezoidFace, out Edge topEdge,
          out Edge leftEdge, out Edge rightEdge)
      {
         PlanarFace face1 = bottomEdge.GetFace(0) as PlanarFace;
         PlanarFace face2 = bottomEdge.GetFace(1) as PlanarFace;

         trapezoidFace = face1 == corbelBottomFace ? face2 : face1;

         EdgeArray trapezoidFaceEdges = trapezoidFace.EdgeLoops.get_Item(0);
         XYZ bottomEdgeDir = (bottomEdge.Evaluate(1.0) - bottomEdge.Evaluate(0.0)).Normalize();
         int bottomEdgeIndex = -1;
         topEdge = null;
         for (int i = 0; i < trapezoidFaceEdges.Size; i++)
         {
            Edge edge = trapezoidFaceEdges.get_Item(i);
            XYZ edgeDir = (edge.Evaluate(1.0) - edge.Evaluate(0.0)).Normalize();
            if (edgeDir.IsAlmostEqualTo(bottomEdgeDir) ||
                edgeDir.IsAlmostEqualTo(-bottomEdgeDir))
            {
               if (edge.Evaluate(0.0).IsAlmostEqualTo(bottomEdge.Evaluate(0.0)))
               {
                  bottomEdge = edge;
                  bottomEdgeIndex = i;
               }
               else
               {
                  topEdge = edge;
               }
            }
         }

         leftEdge = trapezoidFaceEdges.get_Item((trapezoidFaceEdges.Size + bottomEdgeIndex - 1) % trapezoidFaceEdges.Size);
         rightEdge = trapezoidFaceEdges.get_Item((bottomEdgeIndex + 1) % trapezoidFaceEdges.Size);

         XYZ leftEdgeDir = (leftEdge.Evaluate(1.0) - leftEdge.Evaluate(0.0)).Normalize();
         bool isLeftEdgeVertical = false;
         if (leftEdgeDir.IsAlmostEqualTo(hostNormal) ||
             leftEdgeDir.IsAlmostEqualTo(-hostNormal))
         {
            isLeftEdgeVertical = true;
         }

         XYZ rightEdgeDir = (rightEdge.Evaluate(1.0) - rightEdge.Evaluate(0.0)).Normalize();
         bool rightEdgeIsVertical = false;
         if (rightEdgeDir.IsAlmostEqualTo(hostNormal) ||
             rightEdgeDir.IsAlmostEqualTo(-hostNormal))
         {
            rightEdgeIsVertical = true;
         }

         return isLeftEdgeVertical && !rightEdgeIsVertical;
      }

      /// <summary>
      /// Create the CorbelFrame object with the given trapezoid face, corbel and its host information.
      /// </summary>
      /// <param name="corbel">Corbel instance</param>
      /// <param name="depthEdge">Depth Edge which is vertical with trapezoid face</param>
      /// <param name="leftEdge">Left edge of trapezoid</param>
      /// <param name="bottomEdge">Bottom edge of trapezoid</param>
      /// <param name="rightEdge">Right edge of trapezoid</param>
      /// <param name="topEdge">Top edge of trapezoid</param>
      /// <param name="revitDoc">Revit Document</param>
      /// <param name="trapezoidFace">Trapezoid Face</param>
      /// <param name="hostDepth">Corbel Host depth</param>
      /// <param name="hostTopCoverDistance">Corbel Host Top face cover distance</param>
      /// <returns>CorbelFrame object</returns>
      private static CorbelFrame ConstructCorbelFrame(
          FamilyInstance corbel,
          Edge depthEdge, Edge leftEdge, Edge bottomEdge, Edge rightEdge, Edge topEdge,
          Document revitDoc, PlanarFace trapezoidFace,
          double hostDepth, double hostTopCoverDistance)
      {
         XYZ leftEdgeDir = (leftEdge.Evaluate(1.0) - leftEdge.Evaluate(0.0)).Normalize();
         XYZ leftEdgeV0 = leftEdge.Evaluate(0.0);
         Line leftEdgeLine = Line.CreateUnbound(leftEdgeV0, leftEdgeDir);

         XYZ rightEdgeDir = (rightEdge.Evaluate(1.0) - rightEdge.Evaluate(0.0)).Normalize();
         XYZ rightEdgeV0 = rightEdge.Evaluate(0.0);
         Line rightEdgeLine = Line.CreateUnbound(rightEdgeV0, rightEdgeDir);

         XYZ topEdgeDir = (topEdge.Evaluate(1.0) - topEdge.Evaluate(0.0)).Normalize();
         XYZ topEdgeV0 = topEdge.Evaluate(0.0);
         Line topEdgeLine = Line.CreateUnbound(topEdgeV0, topEdgeDir);

         IntersectionResultArray intersections;
         topEdgeLine.Intersect(leftEdgeLine, out intersections);
         XYZ prevX = intersections.get_Item(0).XYZPoint;

         topEdgeLine.Intersect(rightEdgeLine, out intersections);
         XYZ nextX = intersections.get_Item(0).XYZPoint;

         XYZ edgeV0 = GetCommonVertex(bottomEdge, leftEdge);
         XYZ edgeV1 = GetCommonVertex(bottomEdge, rightEdge);

         Line topBoundLine = Line.CreateBound(nextX, prevX);
         Line leftBoundLine = Line.CreateBound(prevX, edgeV0);
         Line bottomBoundLine = Line.CreateBound(edgeV0, edgeV1);
         Line rightBoundLine = Line.CreateBound(edgeV1, nextX);

         Trapezoid profile = new Trapezoid(topBoundLine, leftBoundLine, bottomBoundLine, rightBoundLine);

         XYZ depthEdgeV0 = depthEdge.Evaluate(0.0);
         XYZ depthEdgeV1 = depthEdge.Evaluate(1.0);
         Line depthLine = null;
         if (depthEdgeV0.IsAlmostEqualTo(edgeV0))
         {
            depthLine = Line.CreateBound(depthEdgeV0, depthEdgeV1);
         }
         else if (depthEdgeV1.IsAlmostEqualTo(edgeV0))
         {
            depthLine = Line.CreateBound(depthEdgeV1, depthEdgeV0);
         }

         CorbelFrame frame = new CorbelFrame(corbel, profile, depthLine, hostDepth, hostTopCoverDistance);

         return frame;
      }

      /// <summary>
      /// Get the common vertex XYZ of two edges.
      /// </summary>
      /// <param name="edge1">Edge 1</param>
      /// <param name="edge2">Edge 2</param>
      /// <returns>Common vertex XYZ</returns>
      private static XYZ GetCommonVertex(Edge edge1, Edge edge2)
      {
         XYZ edge1V0 = edge1.Evaluate(0.0);
         XYZ edge1V1 = edge1.Evaluate(1.0);

         XYZ edge2V0 = edge2.Evaluate(0.0);
         XYZ edge2V1 = edge2.Evaluate(1.0);

         if (edge1V0.IsAlmostEqualTo(edge2V0) ||
             edge1V0.IsAlmostEqualTo(edge2V1))
         {
            return edge1V0;
         }
         else if (edge1V1.IsAlmostEqualTo(edge2V0) ||
             edge1V1.IsAlmostEqualTo(edge2V1))
         {
            return edge1V1;
         }
         return null;
      }

      /// <summary>
      /// Extract the Solid of given element.
      /// </summary>
      /// <param name="element">Given Element to get its Solid</param>
      /// <returns>Solid of given element</returns>
      private static Solid GetElementSolid(Element element)
      {
         Options goption = new Options();
         goption.ComputeReferences = true;
         GeometryElement gelem = element.get_Geometry(goption);
         Solid resultSolid = null;
         //foreach (GeometryObject gobj in gelem.Objects)
         IEnumerator<GeometryObject> Objects = gelem.GetEnumerator();
         while (Objects.MoveNext())
         {
            GeometryObject gobj = Objects.Current;

            GeometryInstance gIns = gobj as GeometryInstance;
            if (gIns != null)
            {
               GeometryElement finalGeom = gIns.GetInstanceGeometry();
               //foreach (GeometryObject gobj2 in finalGeom.Objects)
               IEnumerator<GeometryObject> Objects1 = finalGeom.GetEnumerator();
               while (Objects1.MoveNext())
               {
                  GeometryObject gobj2 = Objects1.Current;

                  Solid tSolid = gobj2 as Solid;
                  if (tSolid != null && tSolid.Faces.Size > 0 && tSolid.Volume > 0)
                  {
                     resultSolid = tSolid;
                     break;
                  }
               }
            }

            if (resultSolid == null)
            {
               Solid tSolid2 = gobj as Solid;
               if (tSolid2 != null && tSolid2.Faces.Size > 0 && tSolid2.Volume > 0)
               {
                  resultSolid = tSolid2;
                  break;
               }
            }
         }

         return resultSolid;
      }

      /// <summary>
      /// Compute the outside normal of given face.
      /// </summary>
      /// <param name="face">Given face to get its outside normal</param>
      /// <returns>Outside normal of given face</returns>
      private static XYZ GetNormalOutside(Face face)
      {
         Edge edge = face.EdgeLoops.get_Item(0).get_Item(0);
         UV pt = edge.EvaluateOnFace(0.5, face);
         XYZ faceNormal = face.ComputeNormal(pt);
         return faceNormal;
      }

      /// <summary>
      /// Compute the distance between two planar faces.
      /// </summary>
      /// <param name="face1">Face 1</param>
      /// <param name="face2">Face 2</param>
      /// <returns>Distance of the two planar faces</returns>
      private static double GetDistance(PlanarFace face1, PlanarFace face2)
      {
         BoundingBoxUV boxUV = face2.GetBoundingBox();
         UV center = (boxUV.Max + boxUV.Min) * 0.5;
         XYZ centerPt = face2.Evaluate(center);
         IntersectionResult result = face1.Project(centerPt);
         return face1.Project(centerPt).Distance;
      }
   }
}

SharedParameterUtil.cs

//
// (C) Copyright 2003-2019 by Autodesk, Inc.
//
// Permission to use, copy, modify, and distribute this software in
// object code form for any purpose and without fee is hereby granted,
// provided that the above copyright notice appears in all copies and
// that both that copyright notice and the limited warranty and
// restricted rights notice below appear in all supporting
// documentation.
//
// AUTODESK PROVIDES THIS PROGRAM "AS IS" AND WITH ALL FAULTS.
// AUTODESK SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTY OF
// MERCHANTABILITY OR FITNESS FOR A PARTICULAR USE. AUTODESK, INC.
// DOES NOT WARRANT THAT THE OPERATION OF THE PROGRAM WILL BE
// UNINTERRUPTED OR ERROR FREE.
//
// Use, duplication, or disclosure by the U.S. Government is subject to
// restrictions set forth in FAR 52.227-19 (Commercial Computer
// Software - Restricted Rights) and DFAR 252.227-7013(c)(1)(ii)
// (Rights in Technical Data and Computer Software), as applicable.
//

using System;
using Autodesk.Revit.DB;
using Autodesk.Revit.ApplicationServices;
using Autodesk.Revit.DB.Structure;
using System.Reflection;
using System.IO;

namespace Revit.SDK.Samples.MultiplanarRebar.CS
{
    /// <summary>
    /// This is an utility class used to create shared parameter in Revit Document.
    /// It simplifies the process of shared parameters creation.
    /// </summary>
    class SharedParameterUtil
    {
        /// <summary>
        /// Get existed or create a new shared parameters with the given name and Revit DB Document.
        /// </summary>
        /// <param name="name">Shared parameter name</param>
        /// <param name="revitDoc">Revit DB Document</param>
        /// <returns>ElementId of get or created shared parameter</returns>
        public static ElementId GetOrCreateDef(string name, Document revitDoc)
        {
            ExternalDefinition ed = GetOrCreateDef(name, revitDoc.Application);
            return RebarShapeParameters.GetOrCreateElementIdForExternalDefinition(revitDoc, ed);
        }

        /// <summary>
        /// Get existed or create a new shared parameters with the given name and Revit DB Application.
        /// </summary>
        /// <param name="name">Shared parameter name</param>
        /// <param name="revitApp">Revit DB Application</param>
        /// <returns>ExternalDefinition of get or created shared parameter</returns>
        public static ExternalDefinition GetOrCreateDef(string name, Application revitApp)
        {
            return GetOrCreateDef(name, "Rebar Shape", revitApp);
        }
 
        /// <summary>
        /// Get existed or create a new shared parameters with the given name, group and Revit DB Application.
        /// </summary>
        /// <param name="name">Shared parameter name</param>
        /// <param name="groupName">Shared parameter group name</param>
        /// <param name="revitApp">Revit DB Application</param>
        /// <returns>ExternalDefinition of get or created shared parameter</returns>
        public static ExternalDefinition GetOrCreateDef(string name, string groupName, Application revitApp)
        {
            DefinitionFile parameterFile = GetSharedParameterFile(revitApp);

            DefinitionGroup group = parameterFile.Groups.get_Item(groupName);
            if (group == null)
                group = parameterFile.Groups.Create(groupName);

            ExternalDefinition Bdef = group.Definitions.get_Item(name) as ExternalDefinition;
            if (Bdef == null)
            {
               ExternalDefinitionCreationOptions ExternalDefinitionCreationOptions = new ExternalDefinitionCreationOptions(name, ParameterType.ReinforcementLength);
               Bdef = group.Definitions.Create(ExternalDefinitionCreationOptions) as ExternalDefinition;
            }

            return Bdef;
        }

        /// <summary>
        /// Get shared parameter DefinitionFile of given Revit DB Application.
        /// </summary>
        /// <param name="revitApp">Revit DB Application</param>
        /// <returns>DefinitionFile of Revit DB Application</returns>
        public static DefinitionFile GetSharedParameterFile(Application revitApp)
        {
            DefinitionFile file = null;

            int count = 0;
            // A count is to avoid infinite loop
            while (null == file && count < 100)
            {
                file = revitApp.OpenSharedParameterFile();
                if (file == null)
                {
                    // If Shared parameter file does not exist, then create a new one.
                    string shapeFile = 
                        Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) 
                        + "\\MultiplanarParameterFiles.txt";                        

                    // Fill Schema data of Revit shared parameter file.
                    // If no this schema data, OpenSharedParameterFile may alway return null.
                    System.Text.StringBuilder contents = new System.Text.StringBuilder();
                    contents.AppendLine("# This is a Revit shared parameter file.");
                    contents.AppendLine("# Do not edit manually.");
                    contents.AppendLine("*META	VERSION	MINVERSION");
                    contents.AppendLine("META	2	1");
                    contents.AppendLine("*GROUP	ID	NAME");
                    contents.AppendLine("*PARAM	GUID	NAME	DATATYPE	DATACATEGORY	GROUP	VISIBLE");

                    // Write Schema data of Revit shared parameter file.
                    File.WriteAllText(shapeFile, contents.ToString());

                    // Set Revit shared parameter file
                    revitApp.SharedParametersFilename = shapeFile;
                }

                // To avoid infinite loop.
                ++count;
            }            

            return file;
        }
    }

}