应用程序:NewHostedSweep

Revit平台:建筑

Revit版本:2011.0

首次发布:2009.0

编程语言:C#

技能水平:高级

类别:元素

类型:ExternalCommand

主题:创建基于扫掠线的宿主对象(饰板、排水槽和平板边缘)

摘要:

该示例将演示如何创建基于扫描线的宿主对象(包括饰板、排水槽和平板边缘),以及如何修改它们的属性。

类:

Autodesk.Revit.UI.IExternalCommand

Autodesk.Revit.DB.Document

Autodesk.Revit.Creation.Document

Autodesk.Revit.DB.HostedSweep

Autodesk.Revit.DB.HostedSweepType

Autodesk.Revit.DB.SlabEdge

Autodesk.Revit.DB.SlabEdgeType

Autodesk.Revit.DB.Architecture.Fascia

Autodesk.Revit.DB.Architecture.Gutter

Autodesk.Revit.DB.Architecture.FasciaType

Autodesk.Revit.DB.Architecture.GutterType

Autodesk.Revit.DB.Solid

Autodesk.Revit.DB.Edge

Autodesk.Revit.DB.Reference

Autodesk.Revit.DB.Transform

项目文件:

Command.cs

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

 

HostedSweepCreator.cs

这是一个抽象类,是FasciaCreatorGutterCreatorSlabEdgeCreator的基类。

 

FasciaCreator.cs

该类继承了基类HostedSweepCreator,旨在创建Fascia

 

GutterCreator.cs

该类继承了基类HostedSweepCreator,旨在创建Gutter

 

SlabEdgeCreator.cs

该类继承了基类HostedSweepCreator,旨在创建SlabEdge

 

CreationMgr.cs

这是所有托管横扫创建器的管理器,它包含所有创建器,每个创建器都可以用来创建相应的托管横扫。它的Execute”方法将显示主对话框,供用户创建托管横扫。

 

CreationData.cs

该类包含托管横扫创建的数据。

 

ModificationData.cs

该类包含托管横扫修改的数据。

 

EdgeFetchForm.cs

该窗体旨在为托管横扫的创建或修改获取边缘。它包含一个几何预览的图片框和一个树视图来列出可以在托管横扫上创建的所有边缘。

 

HostedSweepModifyForm.cs

该窗体包含一个属性网格控件来修改托管横扫的属性。

 

MainForm.cs

这是主窗体。它是创建新托管横扫或修改已创建托管横扫的入口。

 

ElementGeometry.cs

该类旨在用C# GDI显示元素的线框。它包含一个元素的固体和一个边境框。它还包含转换(平移、旋转和缩放)来转换几何边缘。

 

TrackBall.cs

该类旨在简化几何变换。它可以生成旋转和缩放变换。

描述:

这个示例让用户创建和修改托管横扫。它的实现如下:

Ø 类型可以通过文档属性FasciaTypesGutterTypesSlabEdgeTypes获得。这些类型可以用于创建FasciaGutterSlabEdge

Ø Fascia将使用NewFascia(FasciaTypeReferenceArray)创建,Gutter将使用NewGutter(GutterTypeReferenceArray)创建,SlabEdge将使用NewSlabEdge(SlabEdgeTypeReferenceArray)创建。第一个参数可以为空(将使用默认类型),也可以是文档中的类型,第二个参数是屋顶或地板的边界引用。

Ø 可以通过每个HostedSweep子类的AddSegmentReference)方法添加段。

Ø 可以通过HostedSweepRemoveSegmentReference)方法删除段。

Ø HostedSweepAngleHorizontalOffsetVerticalOffsetget/set属性,它们的值可以轻松获取和设置。

Ø HostedSweep在水平或垂直方向上的翻转状态可以通过HorizontalFlip()VerticalFlip()方法进行更改。HorizontalFlippedVerticalFlipped属性的属性可以查询水平或垂直方向上的翻转状态。

Ø Revit中立即查看每个操作(例如创建HostedSweep或更改HostedSweep属性)的结果,每个操作都将使用Transaction完成。文档的BeginTransaction()EndTransaction()AbortTransaction()方法将用于执行事务操作。

Ø 边缘是通过AddSegment()方法进行过滤的。如果AddSegment()抛出异常,则该边缘将被丢弃,否则该边缘将被视为合格的边缘。

限制:

为了筛选出托管横扫可以创建的边缘,此示例使用了下面这些方法,这些方法在未来版本中可能会不受支持:

Fascia fascia = NewFascia(null, new ReferenceArray());

Gutter gutter = NewGutter(null, new ReferenceArray());

SlabEdge slabEdge = NewSlabEdge(null, new ReferenceArray());

说明:

1. 打开或新建一个Revit项目,并确保放置了一些屋顶或地板。示例项目文件NewHostedSweep.rvt可在示例文件夹中获得。

2. 运行此命令,主对话框将呈现。

3. 在主对话框中,在组合框列表中选择托管横扫类型,然后单击“创建”按钮。将呈现一个对话框供用户获取边缘并选择托管横扫创建的组合框列表中的一种类型。获取一些边界并选择一种类型后,单击“确定”按钮将创建一个托管横扫,否则单击“取消”按钮将取消此创建操作。

边界获取操作细节:

Ø 如果用户将鼠标悬停在可以创建托管横扫的边界上,则该边界将以黄色突出显示。

Ø 如果用户单击突出显示的边界,则该边界将以红色选择,再次单击可取消选择。

Ø 预览框中的边界选择将反映在边界列表中,反之亦然。

Ø 可通过按下左键并转动来旋转图像,或按下右键来缩放并移动。

Ø 按箭头键也会绕X轴或Y轴旋转几何体。

4. 要修改,请在主对话框中选择托管横扫类型,然后在列表框中选择一个已创建的托管横扫,然后单击“修改”按钮以弹出表单以修改所选横扫的属性。在修改窗体中,用户可以在属性网格中更改值,更改将立即在Revit中反映出来。

5. 用户可以创建多个托管横扫,每个创建的托管横扫将根据类别列在列表框中,并按其类型进行分组。用户单击主窗体中的“确定”按钮后,该命令将退出。

源代码

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

HostedSweepCreator.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.Text;
using Autodesk.Revit.DB;
using Autodesk.Revit.UI;
using Autodesk.Revit;
using System.Windows.Forms;
using System.Collections;

namespace Revit.SDK.Samples.NewHostedSweep.CS
{
   /// <summary>
   /// Provides functions to create hosted sweep and preserves available edges and type.
   /// It is the base class of FasciaCreator, GutterCreator, and SlabEdgeCreator.
   /// </summary>
   public abstract class HostedSweepCreator
   {
      #region Public Interfaces

      /// <summary>
      /// A string indicates which type this creator can create.
      /// </summary>
      virtual public String Name
      {
         get
         {
            return "Hosted Sweep";
         }
      }

      /// <summary>
      /// A dictionary stores all the element=>edges which hosted-sweep can be created on.
      /// </summary>
      public abstract Dictionary<Autodesk.Revit.DB.Element, List<Edge>> SupportEdges
      {
         get;
      }

      /// <summary>
      /// All type of hosted-sweep.
      /// </summary>
      public abstract IEnumerable AllTypes
      {
         get;
      }

      /// <summary>
      /// A dictionary stores all the element=>geometry which hosted-sweep can be created on.
      /// </summary>
      public Dictionary<Autodesk.Revit.DB.Element, ElementGeometry> ElemGeomDic
      {
         get { return m_elemGeom; }
      }

      /// <summary>
      /// Create a hosted-sweep according to the CreationData parameter.
      /// </summary>
      /// <param name="creationData">CreationData parameter</param>
      /// <returns>ModificationData which contains the created hosted-sweep</returns>
      public ModificationData Create(CreationData creationData)
      {
         ReferenceArray refArr = new ReferenceArray();
         foreach (Edge edge in creationData.EdgesForHostedSweep)
         {
            refArr.Append(edge.Reference);
         }

         ModificationData modificationData = null;
         Transaction transaction = new Transaction(m_rvtDoc, "CreateHostedSweep");
         try
         {
            transaction.Start();
            HostedSweep createdHostedSweep = CreateHostedSweep(creationData.Symbol, refArr);

            if (transaction.Commit() == TransactionStatus.Committed)
            {
               m_rvtUIDoc.ShowElements(createdHostedSweep);

               // just only end transaction return true, we will create the hosted sweep.                    
               modificationData =
                   new ModificationData(createdHostedSweep, creationData);
               m_createdHostedSweeps.Add(modificationData);
            }
         }
         catch
         {
            transaction.RollBack();
         }
         return modificationData;
      }

      /// <summary>
      /// A list to store all the created hosted-sweep by this creator.
      /// </summary>
      public List<ModificationData> CreatedHostedSweeps
      {
         get { return m_createdHostedSweeps; }
      }

      /// <summary>
      /// Revit active document.
      /// </summary>
      public Document RvtDocument
      {
         get
         {
            return m_rvtDoc;
         }
      }

      /// <summary>
      /// Revit UI document.
      /// </summary>
      public UIDocument RvtUIDocument
      {
         get
         {
            return m_rvtUIDoc;
         }
      }
      #endregion

      #region Fields and Constructor
      /// <summary>
      /// List of Modification to store all the created hosted-sweep by this.
      /// </summary>
      private List<ModificationData> m_createdHostedSweeps;

      /// <summary>
      /// Revit active document.
      /// </summary>
      protected Document m_rvtDoc;

      /// <summary>
      /// Revit UI document.
      /// </summary>
      protected UIDocument m_rvtUIDoc;

      /// <summary>
      /// Dictionary to store element's geometry which this creator can be used.
      /// </summary>
      protected Dictionary<Autodesk.Revit.DB.Element, ElementGeometry> m_elemGeom;

      /// <summary>
      /// Constructor which takes a Revit.Document as parameter.
      /// </summary>
      /// <param name="rvtDoc">Revit.Document parameter</param>
      protected HostedSweepCreator(Autodesk.Revit.UI.UIDocument rvtDoc)
      {
         m_rvtUIDoc = rvtDoc;
         m_rvtDoc = rvtDoc.Document;
         m_elemGeom = new Dictionary<Autodesk.Revit.DB.Element, ElementGeometry>();
         m_createdHostedSweeps = new List<ModificationData>();
      }
      #endregion

      #region Protected Methods

      /// <summary>
      /// Create a hosted-sweep according to the given Symbol and ReferenceArray.
      /// </summary>
      /// <param name="symbol">Hosted-sweep Symbol</param>
      /// <param name="refArr">Hosted-sweep ReferenceArray</param>
      /// <returns>Created hosted-sweep</returns>
      protected abstract HostedSweep CreateHostedSweep(ElementType symbol, ReferenceArray refArr);

      /// <summary>
      /// Extract the geometry of the given Element.
      /// </summary>
      /// <param name="elem">Element parameter</param>
      /// <returns>Element's geometry</returns>
      protected ElementGeometry ExtractGeom(Autodesk.Revit.DB.Element elem)
      {
         Solid result = null;
         Options options = new Options();
         options.ComputeReferences = true;
         Autodesk.Revit.DB.GeometryElement gElement = elem.get_Geometry(options);
         //foreach (GeometryObject gObj in gElement.Objects)
         IEnumerator<GeometryObject> Objects = gElement.GetEnumerator();
         while (Objects.MoveNext())
         {
            GeometryObject gObj = Objects.Current;

            result = gObj as Solid;
            if (result != null && result.Faces.Size > 0)
               break;
         }
         BoundingBoxXYZ box = elem.get_BoundingBox(null);
         return new ElementGeometry(result, box);
      }
      #endregion
   }
}

FasciaCreator.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.Text;
using Autodesk.Revit.DB;
using Autodesk.Revit.DB.Architecture;
using Autodesk.Revit;
using System.Windows.Forms;
using System.Collections;

namespace Revit.SDK.Samples.NewHostedSweep.CS
{
    /// <summary>
    /// Provides functions to create Fascia.
    /// </summary>
    public class FasciaCreator : HostedSweepCreator
    {
        /// <summary>
        /// Constructor which take Revit.Document as parameter.
        /// </summary>
        /// <param name="rvtDoc">Revit document</param>
        public FasciaCreator(Autodesk.Revit.UI.UIDocument rvtDoc)
            : base(rvtDoc)
        {
        }

        /// <summary>
        /// Dictionary to store the roof=>edges for fascia creation.
        /// </summary>
        private Dictionary<Autodesk.Revit.DB.Element, List<Edge>> m_roofFasciaEdges;

        /// <summary>
        /// Filter all the edges of the given element for fascia creation.
        /// </summary>
        /// <param name="elem">Element used to filter edges which fascia can be created on</param>
        private void FilterEdgesForFascia(Autodesk.Revit.DB.Element elem)
        {
            Transaction transaction = new Transaction(this.RvtDocument, "FilterEdgesForFascia");
            transaction.Start();

            // Note: This method will create a Fascia with no references.
            // In the future, API may not allow to create such Fascia with 
            // no references, invoke this methods like this may throw exception.
            // 
            Fascia fascia = m_rvtDoc.Create.NewFascia(null, new ReferenceArray());

            List<Edge> roofEdges = m_roofFasciaEdges[elem];
            foreach (Edge edge in m_elemGeom[elem].EdgeBindingDic.Keys)
            {
                if (edge.Reference == null) continue;
                try
                {
                    fascia.AddSegment(edge.Reference);
                    // AddSegment successfully, so this edge can be used to crate Fascia.
                    roofEdges.Add(edge);
                }
                catch (Autodesk.Revit.Exceptions.ArgumentOutOfRangeException)
                {
                    // Exception, this edge will be discard.
                }
            }
            // Delete this element, because we just use it to filter the edges.
            m_rvtDoc.Delete(fascia.Id);

            transaction.RollBack();
        }

        /// <summary>
        /// A string indicates this creator just for Roof Fascia creation.
        /// </summary>
        public override string Name
        {
            get
            {
                return "Roof Fascia";
            }
        }

        /// <summary>
        /// All fascia types in Revit active document.
        /// </summary>
        public override IEnumerable AllTypes
        {
            get
            {
                FilteredElementCollector filteredElementCollector = new FilteredElementCollector(m_rvtDoc);
                filteredElementCollector.OfClass(typeof(FasciaType));
                return filteredElementCollector;
            }
        }

        /// <summary>
        /// Dictionary to store all the Roof=>Edges which Fascia can be created on.
        /// </summary>
        public override Dictionary<Autodesk.Revit.DB.Element, List<Edge>> SupportEdges
        {
            get
            {
                if (m_roofFasciaEdges == null)
                {
                    m_roofFasciaEdges = new Dictionary<Autodesk.Revit.DB.Element, List<Edge>>();
                    FilteredElementCollector collector = new FilteredElementCollector(m_rvtDoc);
                    collector.OfClass(typeof(FootPrintRoof));
                    IList<Element> elements = collector.ToElements();

                    collector = new FilteredElementCollector(m_rvtDoc);
                    collector.OfClass(typeof(ExtrusionRoof));
                    foreach (Element elem in collector)
                    {
                        elements.Add(elem);
                    }

                    foreach (Element elem in elements)
                    {
                        if (elem is RoofBase)
                        {
                            ElementGeometry solid = ExtractGeom(elem);
                            if (solid != null)
                            {
                                m_roofFasciaEdges.Add(elem, new List<Edge>());
                                m_elemGeom.Add(elem, solid);
                                FilterEdgesForFascia(elem);
                            }
                        }
                    }
                }
                return m_roofFasciaEdges;
            }
        }

        /// <summary>
        /// Create a Fascia.
        /// </summary>
        /// <param name="symbol">Fascia type</param>
        /// <param name="refArr">Fascia reference array</param>
        /// <returns>Created Fascia</returns>
        protected override HostedSweep CreateHostedSweep(ElementType symbol, ReferenceArray refArr)
        {
            Fascia fascia = m_rvtDoc.Create.NewFascia(symbol as FasciaType, refArr);

            return fascia;
        }
    }
}

GutterCreator.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.Text;
using Autodesk.Revit.DB;
using Autodesk.Revit.DB.Architecture;
using Autodesk.Revit;
using System.Windows.Forms;
using System.Collections;

namespace Revit.SDK.Samples.NewHostedSweep.CS
{
    /// <summary>
    /// Provides functions to create Gutter.
    /// </summary>
    public class GutterCreator : HostedSweepCreator
    {
        /// <summary>
        /// Constructor with Revit.Document as parameter.
        /// </summary>
        /// <param name="rvtDoc">Revit document</param>
        public GutterCreator(Autodesk.Revit.UI.UIDocument rvtDoc)
            : base(rvtDoc)
        {
        }

        /// <summary>
        /// Edges which gutter can be created on.
        /// </summary>
        private Dictionary<Autodesk.Revit.DB.Element, List<Edge>> m_roofGutterEdges;

        /// <summary>
        /// Filter all the edges from the element which gutter can be created on.
        /// </summary>
        /// <param name="elem"></param>
        private void FilterEdgesForGutter(Autodesk.Revit.DB.Element elem)
        {
            Transaction transaction = new Transaction(this.RvtDocument, "FilterEdgesForGutter");
            transaction.Start();

            // Note: This method will create a Gutter with no reference.
            // In the future, API may not allow to create such Gutter with 
            // no references, invoke this methods like this may throw exception.
            // 
            Gutter gutter = m_rvtDoc.Create.NewGutter(null, new ReferenceArray());

            List<Edge> roofEdges = m_roofGutterEdges[elem];
            foreach (Edge edge in m_elemGeom[elem].EdgeBindingDic.Keys)
            {
                if (edge.Reference == null) continue;
                try
                {
                    gutter.AddSegment(edge.Reference);
                    // AddSegment successfully, so this edge can be used to crate Gutter.
                    roofEdges.Add(edge);
                }
                catch (Autodesk.Revit.Exceptions.ArgumentOutOfRangeException)
                {
                    // Exception, this edge will be discard.
                }
            }
            // Delete this element, because we just use it to filter the edges.
            m_rvtDoc.Delete(gutter.Id);

            transaction.RollBack();
        }

        /// <summary>
        /// A string indicates this creator just for Roof Gutter creation.
        /// </summary>
        public override string Name
        {
            get
            {
                return "Roof Gutter";
            }
        }

        /// <summary>
        /// All Gutter types in Revit active document.
        /// </summary>
        public override IEnumerable AllTypes
        {
            get
            {
                FilteredElementCollector filteredElementCollector = new FilteredElementCollector(m_rvtDoc);
                filteredElementCollector.OfClass(typeof(GutterType));
                return filteredElementCollector;
            }
        }

        /// <summary>
        /// Dictionary to store all the Roof=>Edges which Gutter can be created on.
        /// </summary>
        public override Dictionary<Autodesk.Revit.DB.Element, List<Edge>> SupportEdges
        {
            get
            {
                if (m_roofGutterEdges == null)
                {
                    m_roofGutterEdges = new Dictionary<Autodesk.Revit.DB.Element, List<Edge>>();

                    FilteredElementCollector collector = new FilteredElementCollector(m_rvtDoc);
                    collector.OfClass(typeof(FootPrintRoof));
                    IList<Element> elements = collector.ToElements();

                    collector = new FilteredElementCollector(m_rvtDoc);
                    collector.OfClass(typeof(ExtrusionRoof));
                    foreach (Element elem in collector)
                    {
                        elements.Add(elem);
                    }

                    foreach (Element elem in elements)
                    {
                        if (elem is RoofBase)
                        {
                            ElementGeometry solid = ExtractGeom(elem);
                            if (solid != null)
                            {
                                m_roofGutterEdges.Add(elem, new List<Edge>());
                                m_elemGeom.Add(elem, solid);
                                FilterEdgesForGutter(elem);
                            }
                        }
                    }
                }
                return m_roofGutterEdges;
            }
        }

        /// <summary>
        /// Create a Gutter.
        /// </summary>
        /// <param name="symbol">Gutter type</param>
        /// <param name="refArr">Gutter Reference array</param>
        /// <returns>Created Gutter</returns>
        protected override HostedSweep CreateHostedSweep(ElementType symbol, ReferenceArray refArr)
        {
            Gutter gutter = m_rvtDoc.Create.NewGutter(symbol as GutterType, refArr);
            return gutter;
        }
    }
}

SlabEdgeCreator.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.Text;
using Autodesk.Revit.DB;
using Autodesk.Revit;
using System.Windows.Forms;
using System.Collections;

namespace Revit.SDK.Samples.NewHostedSweep.CS
{
    /// <summary>
    /// Provides functions to create SlabEdge.
    /// </summary>
    public class SlabEdgeCreator : HostedSweepCreator
    {
        /// <summary>
        /// Constructor takes Revit.Document as parameter.
        /// </summary>
        /// <param name="rvtDoc">Revit document</param>
        public SlabEdgeCreator(Autodesk.Revit.UI.UIDocument rvtDoc)
            : base(rvtDoc)
        {
        }

        /// <summary>
        /// Edges which SlabEdge can be created on.
        /// </summary>
        private Dictionary<Autodesk.Revit.DB.Element, List<Edge>> m_floorSlabEdges;

        /// <summary>
        /// Filter all the edges from the element which SlabEdge can be created on.
        /// </summary>
        /// <param name="elem"></param>
        private void FilterEdgesForSlabEdge(Autodesk.Revit.DB.Element elem)
        {
            Transaction transaction = new Transaction(this.RvtDocument, "FilterEdgesForSlabEdge");
            transaction.Start();

            // Note: This method will create a SlabEdge with no reference.
            // In the future, API may not allow to create such SlabEdge with 
            // no references, invoke this methods like this may throw exception.
            // 
            SlabEdge slabEdge = m_rvtDoc.Create.NewSlabEdge(null, new ReferenceArray());

            List<Edge> floorEdges = m_floorSlabEdges[elem];
            foreach (Edge edge in m_elemGeom[elem].EdgeBindingDic.Keys)
            {
                if (edge.Reference == null) continue;
                try
                {
                    slabEdge.AddSegment(edge.Reference);
                    // AddSegment successfully, so this edge can be used to crate SlabEdge.
                    floorEdges.Add(edge);
                }
                catch (Autodesk.Revit.Exceptions.ArgumentOutOfRangeException)
                {
                    // Exception, this edge will be discard.
                }
            }
            // Delete this element, because we just use it to filter the edges.
            m_rvtDoc.Delete(slabEdge.Id);

            transaction.RollBack();
        }

        /// <summary>
        /// A string indicates this creator just for Floor SlabEdge creation.
        /// </summary>
        public override string Name
        {
            get
            {
                return "Floor Slab Edge";
            }
        }

        /// <summary>
        /// All SlabEdge types in Revit active document.
        /// </summary>
        public override IEnumerable AllTypes
        {
            get
            {
                FilteredElementCollector filteredElementCollector = new FilteredElementCollector(m_rvtDoc);
                filteredElementCollector.OfClass(typeof(SlabEdgeType));
                return filteredElementCollector;
            }
        }

        /// <summary>
        /// Create a SlabEdge.
        /// </summary>
        /// <param name="symbol">SlabEdge type</param>
        /// <param name="refArr">SlabEdge reference array</param>
        /// <returns>Created SlabEdge</returns>
        protected override HostedSweep CreateHostedSweep(ElementType symbol, ReferenceArray refArr)
        {
            SlabEdge slabEdge = m_rvtDoc.Create.NewSlabEdge(symbol as SlabEdgeType, refArr);
            if (slabEdge != null)
                // Avoid the Revit warning, flip the direction in horizontal direction.
                slabEdge.HorizontalFlip();
            return slabEdge;
        }

        /// <summary>
        /// Dictionary to store all the Floor=>Edges which SlabEdge can be created on.
        /// </summary>
        public override Dictionary<Autodesk.Revit.DB.Element, List<Edge>> SupportEdges
        {
            get
            {
                if (m_floorSlabEdges == null)
                {
                    m_floorSlabEdges = new Dictionary<Autodesk.Revit.DB.Element, List<Edge>>();

                    FilteredElementCollector collector = new FilteredElementCollector(m_rvtDoc);
                    collector.OfClass(typeof(Floor));
                    foreach (Element elem in collector.ToElements())
                    {
                        if (elem is Floor)
                        {
                            ElementGeometry solid = ExtractGeom(elem);
                            if (solid != null)
                            {
                                m_floorSlabEdges.Add(elem, new List<Edge>());
                                m_elemGeom.Add(elem, solid);
                                FilterEdgesForSlabEdge(elem);
                            }
                        }
                    }
                }
                return m_floorSlabEdges;
            }
        }
    }
}

CreationMgr.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.Text;
using System.Windows.Forms;

namespace Revit.SDK.Samples.NewHostedSweep.CS
{
    /// <summary>
    /// This is the manager of all hosted sweep creators, it contains all the creators
    /// and each creator can create the corresponding hosted sweep. Its "Execute" 
    /// method will show the main dialog for user to create hosted sweeps.
    /// </summary>
    public class CreationMgr
    {
        /// <summary>
        /// Revit active document.
        /// </summary>
        private Autodesk.Revit.UI.UIDocument m_rvtDoc;

        /// <summary>
        /// Creator for Fascia.
        /// </summary>
        private FasciaCreator m_fasciaCreator;

        /// <summary>
        /// Creator for Gutter.
        /// </summary>
        private GutterCreator m_gutterCreator;

        /// <summary>
        /// Creator for SlabEdge.
        /// </summary>
        private SlabEdgeCreator m_slabEdgeCreator;

        /// <summary>
        /// Gets Fascia creator.
        /// </summary>
        public FasciaCreator FasciaCreator
        {
            get 
            {
                if(m_fasciaCreator == null)
                {
                    m_fasciaCreator = new FasciaCreator(m_rvtDoc);
                }
                return m_fasciaCreator; 
            }            
        }        

        /// <summary>
        /// Gets Gutter creator.
        /// </summary>
        public GutterCreator GutterCreator
        {
            get 
            {
                if (m_gutterCreator == null)
                {
                    m_gutterCreator = new GutterCreator(m_rvtDoc);
                }                     
                return m_gutterCreator; 
            }            
        }        

        /// <summary>
        /// Gets SlabEdge creator.
        /// </summary>
        public SlabEdgeCreator SlabEdgeCreator
        {
            get 
            {
                if(m_slabEdgeCreator == null)
                {
                    m_slabEdgeCreator = new SlabEdgeCreator(m_rvtDoc);
                }
                return m_slabEdgeCreator;
            }            
        }

        /// <summary>
        /// Constructor.
        /// </summary>
        /// <param name="rvtDoc">Revit active document</param>
        public CreationMgr(Autodesk.Revit.UI.UIDocument rvtDoc)
        {
            m_rvtDoc = rvtDoc;
        }

        /// <summary>
        /// Show the main form, it is the UI entry.
        /// </summary>
        public void Execute()
        {
            using(MainForm mainForm = new MainForm(this))
            {
                mainForm.ShowDialog();
            }
        }
    }
}

CreationData.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.Text;
using Autodesk.Revit;
using Autodesk.Revit.DB;
using System.Drawing.Design;

namespace Revit.SDK.Samples.NewHostedSweep.CS
{
    /// <summary>
    /// This class contains the data for hosted sweep creation.
    /// </summary>
    public class CreationData
    {
        /// <summary>
        /// Represents the method that will handle the EdgeAdded or EdgeRemoved events
        /// of CreationData
        /// </summary>
        /// <param name="edge">Edge</param>
        public delegate void EdgeEventHandler(Edge edge);

        /// <summary>
        /// Represents the method that will handle the SymbolChanged events
        /// of CreationData
        /// </summary>
        /// <param name="sym">Symbol</param>
        public delegate void SymbolChangedEventHandler(ElementType sym);

        /// <summary>
        /// Edge is added to HostedSweep.
        /// </summary>
        public event EdgeEventHandler EdgeAdded;

        /// <summary>
        /// Edge is removed from HostedSweep.
        /// </summary>
        public event EdgeEventHandler EdgeRemoved;

        /// <summary>
        /// HostedSweep symbol is changed.
        /// </summary>
        public event SymbolChangedEventHandler SymbolChanged;

        /// <summary>
        /// Creator contains the necessary data to fetch the edges and get the symbol.
        /// </summary>
        private HostedSweepCreator m_creator;

        /// <summary>
        /// Symbol for HostedSweep creation.
        /// </summary>
        private ElementType m_symbol;

        /// <summary>
        /// Edges which contains references for HostedSweep creation.
        /// </summary>
        private List<Edge> m_edgesForHostedSweep = new List<Edge>();

        /// <summary>
        /// Back up of Symbol.
        /// </summary>
        private ElementType m_backUpSymbol;

        /// <summary>
        /// Back up of Edges.
        /// </summary>
        private List<Edge> m_backUpEdges = new List<Edge>();

        /// <summary>
        /// Constructor.
        /// </summary>
        /// <param name="creator">HostedSweepCreator</param>
        public CreationData(HostedSweepCreator creator)
        {
            m_creator = creator;
        }

        /// <summary>
        /// Back up the Symbol and Edges.
        /// </summary>
        public void BackUp()
        {
            m_backUpSymbol = m_symbol;
            m_backUpEdges.Clear();
            m_backUpEdges.AddRange(m_edgesForHostedSweep);            
        }

        /// <summary>
        /// Restore the Symbol and Edges.
        /// </summary>
        public void Restore()
        {
            m_symbol = m_backUpSymbol;
            m_edgesForHostedSweep.Clear();
            m_edgesForHostedSweep.AddRange(m_backUpEdges);
        }

        /// <summary>
        /// If CreationData changed, notify its observers.
        /// </summary>
        public void Update()
        {
            if (SymbolChanged != null && m_backUpSymbol != null &&
                m_backUpSymbol.Id.IntegerValue != m_symbol.Id.IntegerValue)
                SymbolChanged(m_symbol);

            if (EdgeRemoved != null)
            {
                foreach (Edge edge in m_backUpEdges)
                {
                    if (m_edgesForHostedSweep.IndexOf(edge) == -1)
                    {
                        EdgeRemoved(edge);
                    }
                }
            }

            if(EdgeAdded != null)
            {
                foreach (Edge edge in m_edgesForHostedSweep)
                {
                    if (m_backUpEdges.IndexOf(edge) == -1)
                    {
                        EdgeAdded(edge);
                    }
                }
            }
        }

        /// <summary>
        /// Creator contains the necessary data to fetch the edges and get the symbol.
        /// </summary>
        public HostedSweepCreator Creator
        {
            get { return m_creator; }
        }

        /// <summary>
        /// Symbol for HostedSweep creation.
        /// </summary>
        public ElementType Symbol
        {
            get { return m_symbol; }
            set { m_symbol = value; }
        }        

        /// <summary>
        /// Edges which contains references for HostedSweep creation.
        /// </summary>
        public List<Edge> EdgesForHostedSweep
        {
            get { return m_edgesForHostedSweep; }
        }
    }
}

ModificationData.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.Text;
using Autodesk.Revit.DB;
using Autodesk.Revit.UI;
using Autodesk.Revit;
using Autodesk.Revit.DB.Architecture;
using System.Drawing.Design;
using System.ComponentModel;

namespace Revit.SDK.Samples.NewHostedSweep.CS
{
    /// <summary>
    /// This class contains the data for hosted sweep modification.
    /// </summary>
    public class ModificationData
    {
        /// <summary>
        /// Element to modify.
        /// </summary>
        private HostedSweep m_elemToModify;

        /// <summary>
        /// Creation data can be modified.
        /// </summary>
        private CreationData m_creationData;

        /// <summary>
        /// Revit active document.
        /// </summary>
        private Document m_rvtDoc;

        /// <summary>
        /// Revit UI document.
        /// </summary>
        private UIDocument m_rvtUIDoc;

        /// <summary>
        /// Sub transaction
        /// </summary>
        Transaction m_transaction;


        /// <summary>
        /// Constructor with HostedSweep and CreationData as parameters.
        /// </summary>
        /// <param name="elem">Element to modify</param>
        /// <param name="creationData">CreationData</param>
        public ModificationData(HostedSweep elem, CreationData creationData)
        {
            m_rvtDoc = creationData.Creator.RvtDocument;
            m_rvtUIDoc = creationData.Creator.RvtUIDocument;
            m_elemToModify = elem;
            m_creationData = creationData;

            m_transaction = new Transaction(m_rvtDoc, "External Tool");

            m_creationData.EdgeAdded +=
                new CreationData.EdgeEventHandler(m_creationData_EdgeAdded);
            m_creationData.EdgeRemoved += 
                new CreationData.EdgeEventHandler(m_creationData_EdgeRemoved);
            m_creationData.SymbolChanged += 
                new CreationData.SymbolChangedEventHandler(m_creationData_SymbolChanged);
        }

        /// <summary>
        /// Name of the Creator.
        /// </summary>
        public string CreatorName
        {
            get
            {
                return m_creationData.Creator.Name;
            }
        }

        /// <summary>
        /// Change the symbol of the HostedSweep.
        /// </summary>
        /// <param name="sym"></param>
        private void m_creationData_SymbolChanged(ElementType sym)
        {
            try
            {
                StartTransaction();
                m_elemToModify.ChangeTypeId(sym.Id);
                CommitTransaction();
            }
            catch
            {
                RollbackTransaction();
            }

        }

        /// <summary>
        /// Remove the edge from the HostedSweep.
        /// </summary>
        /// <param name="edge"></param>
        private void m_creationData_EdgeRemoved(Edge edge)
        {
            try
            {
                StartTransaction();
                m_elemToModify.RemoveSegment(edge.Reference);
                CommitTransaction();
            }
            catch
            {
                RollbackTransaction();
            }
        }

        /// <summary>
        /// Add the edge to the HostedSweep.
        /// </summary>
        /// <param name="edge"></param>
        private void m_creationData_EdgeAdded(Edge edge)
        {
            try
            {    
                StartTransaction();
                if (m_elemToModify is Fascia)
                {
                    (m_elemToModify as Fascia).AddSegment(edge.Reference);
                }
                else if (m_elemToModify is Gutter)
                {
                    (m_elemToModify as Gutter).AddSegment(edge.Reference);
                }
                else if (m_elemToModify is SlabEdge)
                {
                    (m_elemToModify as SlabEdge).AddSegment(edge.Reference);
                }
                CommitTransaction();
            }
            catch
            {
                RollbackTransaction();
            }
        }

        /// <summary>
        /// Show the element in a good view.
        /// </summary>
        public void ShowElement()
        {
            try
            {
                StartTransaction();
                m_rvtUIDoc.ShowElements(m_elemToModify);
                CommitTransaction();
            }
            catch
            {
                RollbackTransaction();
            }
        }

        /// <summary>
        /// Name will be displayed in property grid.
        /// </summary>
        [Category("Identity Data")]
        public String Name
        {
            get
            {
                String result = "[Id:" + m_elemToModify.Id.IntegerValue + "] ";
                return result + m_elemToModify.Name;
            }
        }

        /// <summary>
        /// HostedSweep Angle property.
        /// </summary>
        [Category("Profile")]
        public String Angle
        {
            get
            {
                Parameter angle = GetParameter("Angle");
                if (angle != null)
                    return angle.AsValueString();
                else
                    return m_elemToModify.Angle.ToString();
            }
            set
            {
                try
                {
                    StartTransaction();
                    Parameter angle = GetParameter("Angle");
                    if (angle != null)
                        angle.SetValueString(value);
                    else
                        m_elemToModify.Angle = double.Parse(value);
                    CommitTransaction();
                }
                catch
                {
                    RollbackTransaction();
                }
            }
        }

        /// <summary>
        /// HostedSweep profiles edges, the edges can be removed or added in the 
        /// pop up dialog.
        /// </summary>
        [TypeConverter(typeof(CreationDataTypeConverter)),
        Editor(typeof(EdgeFormUITypeEditor), typeof(UITypeEditor)),
        Category("Profile"), DisplayName("Profile Edges")]
        public CreationData AddOrRemoveSegments
        {
            get
            {
                return m_creationData;
            }
        }

        /// <summary>
        /// HostedSweep Length property.
        /// </summary>
        [Category("Dimensions")]
        public string Length
        {            
            get
            {
                Parameter length = GetParameter("Length");
                if (length != null)
                    return length.AsValueString();
                else
                    return m_elemToModify.Length.ToString();
            }
        }

        /// <summary>
        /// HostedSweep HorizontalFlipped property.
        /// </summary>
        [Category("Constraints"), DisplayName("Horizontal Profile Flipped")]
        public bool HorizontalFlipped
        {
            get { return m_elemToModify.HorizontalFlipped; }
            set
            {
                if (value != m_elemToModify.HorizontalFlipped)
                {
                    try
                    {
                        StartTransaction();
                        m_elemToModify.HorizontalFlip();
                        CommitTransaction();
                    }
                    catch
                    {
                        RollbackTransaction();
                    }
                }
            }
        }

        /// <summary>
        /// HostedSweep HorizontalOffset property.
        /// </summary>
        [Category("Constraints"), DisplayName("Horizontal Profile Offset")]
        public String HorizontalOffset
        {
            get
            {
                Parameter horiOff = GetParameter("Horizontal Profile Offset");
                if (horiOff != null)
                    return horiOff.AsValueString();
                else
                    return m_elemToModify.HorizontalOffset.ToString(); 
            }
            set
            {
                try
                {
                    StartTransaction();
                    Parameter horiOff = GetParameter("Horizontal Profile Offset");
                    if (horiOff != null)
                        horiOff.SetValueString(value);
                    else
                        m_elemToModify.HorizontalOffset = double.Parse(value); 
                    CommitTransaction();
                }
                catch
                {
                    RollbackTransaction();
                }
            }
        }

        /// <summary>
        /// HostedSweep VerticalFlipped property.
        /// </summary>
        [Category("Constraints"), DisplayName("Vertical Profile Flipped")]
        public bool VerticalFlipped
        {
            get
            { 
                return m_elemToModify.VerticalFlipped; 
            }
            set
            {
                if (value != m_elemToModify.VerticalFlipped)
                {
                    try
                    {
                        StartTransaction();
                        m_elemToModify.VerticalFlip();
                        CommitTransaction();
                    }
                    catch
                    {
                        RollbackTransaction();
                    }
                }
            }
        }

        /// <summary>
        /// HostedSweep VerticalOffset property.
        /// </summary>
        [Category("Constraints"), DisplayName("Vertical Profile Offset")]
        public String VerticalOffset
        {
            get
            {
                Parameter vertOff = GetParameter("Vertical Profile Offset");
                if (vertOff != null)
                    return vertOff.AsValueString();
                else
                    return m_elemToModify.VerticalOffset.ToString(); 
            }
            set 
            {
                try
                {
                    StartTransaction();
                    Parameter vertOff = GetParameter("Vertical Profile Offset");
                    if (vertOff != null)
                        vertOff.SetValueString(value);
                    else 
                        m_elemToModify.VerticalOffset = double.Parse(value); 
                    CommitTransaction();
                }
                catch
                {
                    RollbackTransaction();
                }
            }
        }

        /// <summary>
        /// Get parameter by given name.
        /// </summary>
        /// <param name="name">name of parameter</param>
        /// <returns>parameter whose definition name is the given name.</returns>
        protected Parameter GetParameter(String name)
        {
            return m_elemToModify.LookupParameter(name);
        }


        public TransactionStatus StartTransaction()
        {
            return m_transaction.Start();
        }

        public TransactionStatus CommitTransaction()
        {
            return m_transaction.Commit();
        }

        public TransactionStatus RollbackTransaction()
        {
            return m_transaction.RollBack();
        }
    }
}

EdgeFetchForm.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.
//

namespace Revit.SDK.Samples.NewHostedSweep.CS
{
    partial class EdgeFetchForm
    {
        /// <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.components = new System.ComponentModel.Container();
            System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(EdgeFetchForm));
            this.comboBoxTypes = new System.Windows.Forms.ComboBox();
            this.groupBoxEdges = new System.Windows.Forms.GroupBox();
            this.treeViewHost = new System.Windows.Forms.TreeView();
            this.buttonOK = new System.Windows.Forms.Button();
            this.buttonCancel = new System.Windows.Forms.Button();
            this.groupBox1 = new System.Windows.Forms.GroupBox();
            this.pictureBoxPreview = new System.Windows.Forms.PictureBox();
            this.label = new System.Windows.Forms.Label();
            this.imageListForCheckBox = new System.Windows.Forms.ImageList(this.components);
            this.labelHit = new System.Windows.Forms.Label();
            this.groupBoxEdges.SuspendLayout();
            this.groupBox1.SuspendLayout();
            ((System.ComponentModel.ISupportInitialize)(this.pictureBoxPreview)).BeginInit();
            this.SuspendLayout();
            // 
            // comboBoxTypes
            // 
            this.comboBoxTypes.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
            this.comboBoxTypes.FormattingEnabled = true;
            this.comboBoxTypes.Location = new System.Drawing.Point(12, 38);
            this.comboBoxTypes.Name = "comboBoxTypes";
            this.comboBoxTypes.Size = new System.Drawing.Size(207, 21);
            this.comboBoxTypes.TabIndex = 3;
            // 
            // groupBoxEdges
            // 
            this.groupBoxEdges.Controls.Add(this.treeViewHost);
            this.groupBoxEdges.Location = new System.Drawing.Point(12, 65);
            this.groupBoxEdges.Name = "groupBoxEdges";
            this.groupBoxEdges.Size = new System.Drawing.Size(207, 610);
            this.groupBoxEdges.TabIndex = 4;
            this.groupBoxEdges.TabStop = false;
            this.groupBoxEdges.Text = "Extract Edges for HostedSweep";
            // 
            // treeViewHost
            // 
            this.treeViewHost.Dock = System.Windows.Forms.DockStyle.Fill;
            this.treeViewHost.Location = new System.Drawing.Point(3, 16);
            this.treeViewHost.Name = "treeViewHost";
            this.treeViewHost.Size = new System.Drawing.Size(201, 591);
            this.treeViewHost.TabIndex = 1;
            this.treeViewHost.BeforeExpand += new System.Windows.Forms.TreeViewCancelEventHandler(this.treeViewHost_BeforeExpand);
            this.treeViewHost.NodeMouseHover += new System.Windows.Forms.TreeNodeMouseHoverEventHandler(this.treeViewHost_NodeMouseHover);
            this.treeViewHost.BeforeCollapse += new System.Windows.Forms.TreeViewCancelEventHandler(this.treeViewHost_BeforeCollapse);
            this.treeViewHost.KeyDown += new System.Windows.Forms.KeyEventHandler(this.treeViewHost_KeyDown);
            this.treeViewHost.MouseDown += new System.Windows.Forms.MouseEventHandler(this.treeViewHost_MouseDown);
            // 
            // buttonOK
            // 
            this.buttonOK.Location = new System.Drawing.Point(28, 681);
            this.buttonOK.Name = "buttonOK";
            this.buttonOK.Size = new System.Drawing.Size(75, 23);
            this.buttonOK.TabIndex = 5;
            this.buttonOK.Text = "&OK";
            this.buttonOK.UseVisualStyleBackColor = true;
            this.buttonOK.Click += new System.EventHandler(this.buttonOK_Click);
            // 
            // buttonCancel
            // 
            this.buttonCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel;
            this.buttonCancel.Location = new System.Drawing.Point(125, 681);
            this.buttonCancel.Name = "buttonCancel";
            this.buttonCancel.Size = new System.Drawing.Size(75, 23);
            this.buttonCancel.TabIndex = 6;
            this.buttonCancel.Text = "&Cancel";
            this.buttonCancel.UseVisualStyleBackColor = true;
            this.buttonCancel.Click += new System.EventHandler(this.buttonCancel_Click);
            // 
            // groupBox1
            // 
            this.groupBox1.Controls.Add(this.pictureBoxPreview);
            this.groupBox1.Location = new System.Drawing.Point(225, 11);
            this.groupBox1.Name = "groupBox1";
            this.groupBox1.Size = new System.Drawing.Size(604, 615);
            this.groupBox1.TabIndex = 8;
            this.groupBox1.TabStop = false;
            this.groupBox1.Text = "Preview";
            // 
            // pictureBoxPreview
            // 
            this.pictureBoxPreview.BackColor = System.Drawing.SystemColors.WindowText;
            this.pictureBoxPreview.Dock = System.Windows.Forms.DockStyle.Fill;
            this.pictureBoxPreview.Location = new System.Drawing.Point(3, 16);
            this.pictureBoxPreview.Name = "pictureBoxPreview";
            this.pictureBoxPreview.Size = new System.Drawing.Size(598, 596);
            this.pictureBoxPreview.TabIndex = 10;
            this.pictureBoxPreview.TabStop = false;
            this.pictureBoxPreview.MouseDown += new System.Windows.Forms.MouseEventHandler(this.pictureBoxPreview_MouseDown);
            this.pictureBoxPreview.MouseMove += new System.Windows.Forms.MouseEventHandler(this.pictureBoxPreview_MouseMove);
            this.pictureBoxPreview.Paint += new System.Windows.Forms.PaintEventHandler(this.pictureBoxPreview_Paint);
            this.pictureBoxPreview.MouseClick += new System.Windows.Forms.MouseEventHandler(this.pictureBoxPreview_MouseClick);
            // 
            // label
            // 
            this.label.AutoSize = true;
            this.label.Location = new System.Drawing.Point(15, 11);
            this.label.Name = "label";
            this.label.Size = new System.Drawing.Size(157, 13);
            this.label.TabIndex = 9;
            this.label.Text = "Select a type for HostedSweep:";
            // 
            // imageListForCheckBox
            // 
            this.imageListForCheckBox.ImageStream = ((System.Windows.Forms.ImageListStreamer)(resources.GetObject("imageListForCheckBox.ImageStream")));
            this.imageListForCheckBox.TransparentColor = System.Drawing.Color.Transparent;
            this.imageListForCheckBox.Images.SetKeyName(0, "CBUnchecked.bmp");
            this.imageListForCheckBox.Images.SetKeyName(1, "CBchecked.bmp");
            this.imageListForCheckBox.Images.SetKeyName(2, "CBIndeterminate.bmp");
            // 
            // labelHit
            // 
            this.labelHit.AutoSize = true;
            this.labelHit.Location = new System.Drawing.Point(226, 633);
            this.labelHit.Name = "labelHit";
            this.labelHit.Size = new System.Drawing.Size(527, 52);
            this.labelHit.TabIndex = 10;
            this.labelHit.Text = resources.GetString("labelHit.Text");
            // 
            // EdgeFetchForm
            // 
            this.AcceptButton = this.buttonOK;
            this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
            this.CancelButton = this.buttonCancel;
            this.ClientSize = new System.Drawing.Size(834, 716);
            this.Controls.Add(this.labelHit);
            this.Controls.Add(this.label);
            this.Controls.Add(this.groupBox1);
            this.Controls.Add(this.buttonCancel);
            this.Controls.Add(this.buttonOK);
            this.Controls.Add(this.groupBoxEdges);
            this.Controls.Add(this.comboBoxTypes);
            this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog;
            this.MaximizeBox = false;
            this.MinimizeBox = false;
            this.Name = "EdgeFetchForm";
            this.ShowInTaskbar = false;
            this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
            this.Text = "Extract edges for Hosted Sweep";
            this.KeyDown += new System.Windows.Forms.KeyEventHandler(this.EdgeFetch_KeyDown);
            this.Load += new System.EventHandler(this.EdgeFetch_Load);
            this.groupBoxEdges.ResumeLayout(false);
            this.groupBox1.ResumeLayout(false);
            ((System.ComponentModel.ISupportInitialize)(this.pictureBoxPreview)).EndInit();
            this.ResumeLayout(false);
            this.PerformLayout();

        }

        #endregion

        private System.Windows.Forms.ComboBox comboBoxTypes;
        private System.Windows.Forms.GroupBox groupBoxEdges;
        private System.Windows.Forms.Button buttonOK;
        private System.Windows.Forms.Button buttonCancel;
        private System.Windows.Forms.GroupBox groupBox1;
        private System.Windows.Forms.TreeView treeViewHost;
        private System.Windows.Forms.Label label;
        private System.Windows.Forms.ImageList imageListForCheckBox;
        private System.Windows.Forms.PictureBox pictureBoxPreview;
        private System.Windows.Forms.Label labelHit;
    }
}

HostedSweepModifyForm.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.
//

namespace Revit.SDK.Samples.NewHostedSweep.CS
{
    partial class HostedSweepModifyForm
    {
        /// <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.components = new System.ComponentModel.Container();
            System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(HostedSweepModifyForm));
            this.buttonOK = new System.Windows.Forms.Button();
            this.imageList1 = new System.Windows.Forms.ImageList(this.components);
            this.propertyGrid = new System.Windows.Forms.PropertyGrid();
            this.SuspendLayout();
            // 
            // buttonOK
            // 
            this.buttonOK.DialogResult = System.Windows.Forms.DialogResult.Cancel;
            this.buttonOK.Location = new System.Drawing.Point(110, 451);
            this.buttonOK.Name = "buttonOK";
            this.buttonOK.Size = new System.Drawing.Size(75, 23);
            this.buttonOK.TabIndex = 2;
            this.buttonOK.Text = "&OK";
            this.buttonOK.UseVisualStyleBackColor = true;
            this.buttonOK.Click += new System.EventHandler(this.buttonOK_Click);
            // 
            // imageList1
            // 
            this.imageList1.ImageStream = ((System.Windows.Forms.ImageListStreamer)(resources.GetObject("imageList1.ImageStream")));
            this.imageList1.TransparentColor = System.Drawing.Color.Transparent;
            this.imageList1.Images.SetKeyName(0, "icon_zoomOut.gif");
            this.imageList1.Images.SetKeyName(1, "icon_zoomIn.gif");
            this.imageList1.Images.SetKeyName(2, "nav_band_contract.gif");
            this.imageList1.Images.SetKeyName(3, "nav_band_expand.gif");
            // 
            // propertyGrid
            // 
            this.propertyGrid.Location = new System.Drawing.Point(10, 12);
            this.propertyGrid.Name = "propertyGrid";
            this.propertyGrid.Size = new System.Drawing.Size(295, 433);
            this.propertyGrid.TabIndex = 4;
            // 
            // HostedSweepModify
            // 
            this.AcceptButton = this.buttonOK;
            this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
            this.CancelButton = this.buttonOK;
            this.ClientSize = new System.Drawing.Size(315, 486);
            this.Controls.Add(this.propertyGrid);
            this.Controls.Add(this.buttonOK);
            this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog;
            this.MaximizeBox = false;
            this.MinimizeBox = false;
            this.Name = "HostedSweepModify";
            this.ShowInTaskbar = false;
            this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
            this.Text = "Modify Hosted Sweep";
            this.Load += new System.EventHandler(this.HostedSweepModify_Load);
            this.ResumeLayout(false);

        }

        #endregion

        private System.Windows.Forms.Button buttonOK;
        private System.Windows.Forms.ImageList imageList1;
        private System.Windows.Forms.PropertyGrid propertyGrid;
    }
}

MainForm.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.
//

namespace Revit.SDK.Samples.NewHostedSweep.CS
{
    partial class MainForm
    {
        /// <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.buttonCreate = new System.Windows.Forms.Button();
            this.buttonModify = new System.Windows.Forms.Button();
            this.listBoxCreatedHostedSweeps = new System.Windows.Forms.ListBox();
            this.buttonOK = new System.Windows.Forms.Button();
            this.comboBoxHostedSweepType = new System.Windows.Forms.ComboBox();
            this.label1 = new System.Windows.Forms.Label();
            this.label2 = new System.Windows.Forms.Label();
            this.SuspendLayout();
            // 
            // buttonCreate
            // 
            this.buttonCreate.Anchor = System.Windows.Forms.AnchorStyles.Bottom;
            this.buttonCreate.Location = new System.Drawing.Point(12, 295);
            this.buttonCreate.Name = "buttonCreate";
            this.buttonCreate.Size = new System.Drawing.Size(68, 23);
            this.buttonCreate.TabIndex = 1;
            this.buttonCreate.Text = "&Create...";
            this.buttonCreate.UseVisualStyleBackColor = true;
            this.buttonCreate.Click += new System.EventHandler(this.buttonCreate_Click);
            // 
            // buttonModify
            // 
            this.buttonModify.Anchor = System.Windows.Forms.AnchorStyles.Bottom;
            this.buttonModify.Enabled = false;
            this.buttonModify.Location = new System.Drawing.Point(86, 295);
            this.buttonModify.Name = "buttonModify";
            this.buttonModify.Size = new System.Drawing.Size(68, 23);
            this.buttonModify.TabIndex = 3;
            this.buttonModify.Text = "&Modify...";
            this.buttonModify.UseVisualStyleBackColor = true;
            this.buttonModify.Click += new System.EventHandler(this.buttonModify_Click);
            // 
            // listBoxCreatedHostedSweeps
            // 
            this.listBoxCreatedHostedSweeps.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
                        | System.Windows.Forms.AnchorStyles.Left)
                        | System.Windows.Forms.AnchorStyles.Right)));
            this.listBoxCreatedHostedSweeps.FormattingEnabled = true;
            this.listBoxCreatedHostedSweeps.Location = new System.Drawing.Point(12, 87);
            this.listBoxCreatedHostedSweeps.Name = "listBoxCreatedHostedSweeps";
            this.listBoxCreatedHostedSweeps.Size = new System.Drawing.Size(216, 199);
            this.listBoxCreatedHostedSweeps.TabIndex = 4;
            this.listBoxCreatedHostedSweeps.SelectedValueChanged += new System.EventHandler(this.listBoxHostedSweeps_SelectedValueChanged);
            // 
            // buttonOK
            // 
            this.buttonOK.Anchor = System.Windows.Forms.AnchorStyles.Bottom;
            this.buttonOK.DialogResult = System.Windows.Forms.DialogResult.Cancel;
            this.buttonOK.Location = new System.Drawing.Point(160, 295);
            this.buttonOK.Name = "buttonOK";
            this.buttonOK.Size = new System.Drawing.Size(68, 23);
            this.buttonOK.TabIndex = 5;
            this.buttonOK.Text = "&OK";
            this.buttonOK.UseVisualStyleBackColor = true;
            this.buttonOK.Click += new System.EventHandler(this.buttonOK_Click);
            // 
            // comboBoxHostedSweepType
            // 
            this.comboBoxHostedSweepType.FormattingEnabled = true;
            this.comboBoxHostedSweepType.Location = new System.Drawing.Point(12, 31);
            this.comboBoxHostedSweepType.Name = "comboBoxHostedSweepType";
            this.comboBoxHostedSweepType.Size = new System.Drawing.Size(216, 21);
            this.comboBoxHostedSweepType.TabIndex = 6;
            this.comboBoxHostedSweepType.SelectedIndexChanged += new System.EventHandler(this.comboBoxHostedSweepType_SelectedIndexChanged);
            // 
            // label1
            // 
            this.label1.AutoSize = true;
            this.label1.Location = new System.Drawing.Point(9, 9);
            this.label1.Name = "label1";
            this.label1.Size = new System.Drawing.Size(141, 13);
            this.label1.TabIndex = 7;
            this.label1.Text = "Select a hosted sweep type:";
            // 
            // label2
            // 
            this.label2.AutoSize = true;
            this.label2.Location = new System.Drawing.Point(12, 66);
            this.label2.Name = "label2";
            this.label2.Size = new System.Drawing.Size(202, 13);
            this.label2.TabIndex = 8;
            this.label2.Text = "Select a created hosted sweep to modify:";
            // 
            // MainForm
            // 
            this.AcceptButton = this.buttonOK;
            this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
            this.CancelButton = this.buttonOK;
            this.ClientSize = new System.Drawing.Size(240, 330);
            this.Controls.Add(this.label2);
            this.Controls.Add(this.label1);
            this.Controls.Add(this.listBoxCreatedHostedSweeps);
            this.Controls.Add(this.comboBoxHostedSweepType);
            this.Controls.Add(this.buttonOK);
            this.Controls.Add(this.buttonModify);
            this.Controls.Add(this.buttonCreate);
            this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog;
            this.MaximizeBox = false;
            this.MinimizeBox = false;
            this.Name = "MainForm";
            this.ShowInTaskbar = false;
            this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
            this.Text = "Hosted Sweep";
            this.Load += new System.EventHandler(this.MainForm_Load);
            this.ResumeLayout(false);
            this.PerformLayout();

        }

        #endregion

        private System.Windows.Forms.Button buttonCreate;
        private System.Windows.Forms.Button buttonModify;
        private System.Windows.Forms.Button buttonOK;
        private System.Windows.Forms.ListBox listBoxCreatedHostedSweeps;
        private System.Windows.Forms.ComboBox comboBoxHostedSweepType;
        private System.Windows.Forms.Label label1;
        private System.Windows.Forms.Label label2;

    }
}

ElementGeometry.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.Text;
using Autodesk.Revit.DB;
using System.Drawing;
using System.Drawing.Drawing2D;

namespace Revit.SDK.Samples.NewHostedSweep.CS
{
    /// <summary>
    /// This class is intent to display element's wire-frame with C# GDI.
    /// It contains a solid and a bounding box of an element.
    /// It also contains transformation (translation, rotation and scale) to
    /// transform the geometry edges.
    /// </summary>
    public class ElementGeometry
    {
        /// <summary>
        /// Element's Solid
        /// </summary>
        private Solid m_solid;

        /// <summary>
        /// Solid bounding box minimal corner.
        /// </summary>
        private XYZ m_bBoxMin;

        /// <summary>
        /// Solid bounding box maximal corner.
        /// </summary>
        private XYZ m_bBoxMax;

        /// <summary>
        /// Translation transform, it is intent to translate the solid.
        /// It is actually the center of Bounding box.
        /// </summary>
        private XYZ m_translation;

        /// <summary>
        /// Scale transform, it is intent to scale the solid.
        /// </summary>
        private double m_scale;

        /// <summary>
        /// Rotation transform, it is intent to rotate the solid.
        /// </summary>
        private Transform m_rotation;

        /// <summary>
        /// If the solid is transformed (includes translation, scale and rotation)
        /// this flag should be true, otherwise false.
        /// </summary>
        private bool m_isDirty;

        /// <summary>
        /// Solid's Edge to EdgeBinding dictionary. It is intent to store all the edges
        /// of solid.
        /// </summary>
        private Dictionary<Edge, EdgeBinding> m_edgeBindinDic;

        /// <summary>
        /// Translation transform, it is intent to translate the solid.
        /// It is actually the center of Bounding box.
        /// </summary>
        public XYZ Translation
        {
            get { return m_translation; }
            set
            {
                m_isDirty = true;
                m_translation = value;
            }
        }

        /// <summary>
        /// Scale transform, it is intent to scale the solid.
        /// </summary>
        public double Scale
        {
            get { return m_scale; }
            set
            {
                m_isDirty = true;
                m_scale = value;
            }
        }

        /// <summary>
        /// Rotation transform, it is intent to rotate the solid.
        /// </summary>
        public Transform Rotation
        {
            get { return m_rotation; }
            set
            {
                m_isDirty = true;
                m_rotation = value;
            }
        }

        /// <summary>
        /// Element's Solid
        /// </summary>
        public Solid Solid
        {
            get { return m_solid; }
        }

        /// <summary>
        /// Solid's Edge to EdgeBinding dictionary. It is intent to store all the edges
        /// of solid.
        /// </summary>
        public Dictionary<Edge, EdgeBinding> EdgeBindingDic
        {
            get { return m_edgeBindinDic; }
        }

        /// <summary>
        /// Constructor, Construct a new object with an element's geometry Solid,
        /// and its corresponding bounding box.
        /// </summary>
        /// <param name="solid">Element's geometry Solid</param>
        /// <param name="box">Element's geometry bounding box</param>
        public ElementGeometry(Solid solid, BoundingBoxXYZ box)
        {
            m_solid = solid;
            m_bBoxMin = box.Min;
            m_bBoxMax = box.Max;
            m_isDirty = true;

            // Initialize edge binding
            m_edgeBindinDic = new Dictionary<Edge, EdgeBinding>();
            foreach (Edge edge in m_solid.Edges)
            {
                EdgeBinding edgeBingding = new EdgeBinding(edge);
                m_edgeBindinDic.Add(edge, edgeBingding);
            }
        }

        /// <summary>
        /// Initialize the transform (includes translation, scale, and rotation).
        /// </summary>
        /// <param name="width">Width of the view</param>
        /// <param name="height">Height of the view</param>
        public void InitializeTransform(double width, double height)
        {
            // Initialize translation and rotation transform
            XYZ bBoxCenter = (m_bBoxMax + m_bBoxMin) / 2.0;
            m_translation = -bBoxCenter;
            m_rotation = Transform.Identity;
            
            // Initialize scale factor
            double bBoxWidth = m_bBoxMax.X - m_bBoxMin.X;
            double bBoxHeight = m_bBoxMax.Y - m_bBoxMin.Y;
            double widthScale = width / bBoxWidth;
            double heigthScale = height / bBoxHeight;
            m_scale = Math.Min(widthScale, heigthScale);

            // Set dirty flag
            m_isDirty = true;
        }

        /// <summary>
        /// Reset all the edges' status to their original status.
        /// </summary>
        public void ResetEdgeStates()
        {
            foreach (KeyValuePair<Edge, EdgeBinding> pair in m_edgeBindinDic)
            {
                pair.Value.Reset();
            }
        }

        /// <summary>
        /// Update all the edges' transform (include translation, scale, and rotation),
        /// reconstruct the edge's geometry info.
        /// </summary>
        private void Update()
        {
            if (!m_isDirty) return;

            foreach (KeyValuePair<Edge, EdgeBinding> pair in m_edgeBindinDic)
            {
                pair.Value.Update(m_rotation, m_translation, m_scale);
            }
            m_isDirty = false;
        }

        /// <summary>
        /// Draw all the edges of solid in Graphics.
        /// </summary>
        /// <param name="g">Graphics, edges will be draw in it</param>
        public void Draw(Graphics g)
        {
            Update();
            foreach (KeyValuePair<Edge, EdgeBinding> pair in m_edgeBindinDic)
            {
                pair.Value.Draw(g);
            }
        }
    }

    /// <summary>
    /// Binds an edge with some properties which contains its geometry information 
    /// and indicates whether the edge is selected or highlighted.
    /// </summary>
    public class EdgeBinding : IDisposable
    {
        /// <summary>
        /// Edge points in world coordinate system of Revit.
        /// </summary>
        private IList<XYZ> m_points;

        /// <summary>
        /// Edge geometry presentation in C# GDI.
        /// </summary>
        private GraphicsPath m_gdiEdge;

        /// <summary>
        /// Edge bounding Region used to hit testing.
        /// </summary>
        private Region m_region;

        /// <summary>
        /// Pen for edge display.
        /// </summary>
        private Pen m_pen;

        /// <summary>
        /// A flag to indicate the edge is highlighted or not.
        /// </summary>
        private bool m_isHighLighted;

        /// <summary>
        /// A flag to indicate the edge is selected or not.
        /// </summary>
        private bool m_isSelected;

        /// <summary>
        /// Gets whether the edge is highlighted or not.
        /// </summary>
        public bool IsHighLighted
        {
            get { return m_isHighLighted; }
            set { m_isHighLighted = value; }
        }

        /// <summary>
        /// Gets whether the edge is selected or not.
        /// </summary>
        public bool IsSelected
        {
            get { return m_isSelected; }
            set { m_isSelected = value; }
        }

        /// <summary>
        /// Constructor takes Edge as parameter.
        /// </summary>
        /// <param name="edge">Edge</param>
        public EdgeBinding(Edge edge)
        {
            m_points = edge.Tessellate();
            m_pen = new Pen(System.Drawing.Color.White);
            Reset();
        }

        /// <summary>
        /// Reset the status of the edge: un-highlighted, un-selected
        /// </summary>
        public void Reset()
        {
            m_isHighLighted = false;
            m_isSelected = false;
            m_region = null;
        }

        /// <summary>
        /// Update the edge's geometry according to the transformation.
        /// </summary>
        /// <param name="rotation">Rotation transform</param>
        /// <param name="translation">Translation transform</param>
        /// <param name="scale">Scale transform</param>
        public void Update(Transform rotation, XYZ translation, double scale)
        {
            rotation = rotation.Inverse;
            PointF[] points = new PointF[m_points.Count];
            for (int i = 0; i < m_points.Count; i++)
            {
                XYZ tmpPt = m_points[i];
                tmpPt = rotation.OfPoint((tmpPt + translation) * scale);
                points[i] = new PointF((float)tmpPt.X, (float)tmpPt.Y);
            }
            if (m_gdiEdge != null) m_gdiEdge.Dispose();
            m_gdiEdge = new GraphicsPath();
            m_gdiEdge.AddLines(points);

            if (m_region != null) m_region.Dispose();
            m_region = null;
        }

        /// <summary>
        /// Draw the edge in Graphics.
        /// </summary>
        /// <param name="g">Graphics</param>
        public void Draw(Graphics g)
        {
            m_pen.Width = 2.0f;
            if (m_isHighLighted)
            {
                m_pen.Color = System.Drawing.Color.Yellow;                
            }
            else if (m_isSelected)
            {
                m_pen.Color = System.Drawing.Color.Red;
            }
            else
            {
                m_pen.Color = System.Drawing.Color.Green;
            }
            g.DrawPath(m_pen, m_gdiEdge);
        }

        /// <summary>
        /// Return the Edge Region.
        /// </summary>
        /// <returns>Region of the edge</returns>
        private Region GetRegion()
        {
            if (m_region == null)
            {
                GraphicsPath tmpPath = new GraphicsPath();
                tmpPath.AddLines(m_gdiEdge.PathPoints);
                Pen tmpPen = new Pen(System.Drawing.Color.White, 3.0f);
                tmpPath.Widen(tmpPen);
                m_region = new Region(tmpPath);
            }
            return m_region;
        }

        /// <summary>
        /// Test whether or not the edge is under a specified location.
        /// </summary>
        /// <param name="x">X coordinate</param>
        /// <param name="y">Y coordinate</param>
        /// <returns></returns>
        private bool HitTest(float x, float y)
        {
            return this.GetRegion().IsVisible(x, y);
        }

        /// <summary>
        /// If the edge under the location (x, y), set the highlight flag to true,
        /// otherwise false.
        /// </summary>
        /// <param name="x">X coordinate</param>
        /// <param name="y">Y coordinate</param>
        /// <returns></returns>
        public bool HighLight(float x, float y)
        {
            m_isHighLighted = HitTest(x, y);
            return m_isHighLighted;
        }

        #region IDisposable Members

        /// <summary>
        /// Dispose
        /// </summary>
        public void Dispose()
        {
            m_gdiEdge.Dispose();
            m_pen.Dispose();
            m_region.Dispose();
        }

        #endregion
    }
}

TrackBall.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.Text;
using System.Drawing;
using Autodesk.Revit.DB;
using System.Windows.Forms;
using Point = System.Drawing.Point;

namespace Revit.SDK.Samples.NewHostedSweep.CS
{
    /// <summary>
    /// This class is intent to convenience the geometry transformations.
    /// It can produce rotation and scale transformations. 
    /// </summary>
    public class TrackBall
    {
        /// <summary>
        /// Canvas width.
        /// </summary>
        private float m_canvasWidth;

        /// <summary>
        /// Canvas height.
        /// </summary>
        private float m_canvasHeight;

        /// <summary>
        /// Previous position in 2D.
        /// </summary>
        private Point m_previousPosition2D;

        /// <summary>
        /// Previous position in 3D.
        /// </summary>
        private XYZ m_previousPosition3D;

        /// <summary>
        /// Current rotation transform.
        /// </summary>
        private Transform m_rotation = Transform.Identity;

        /// <summary>
        /// Current scale transform.
        /// </summary>
        private double m_scale;

        /// <summary>
        /// Current rotation transform.
        /// </summary>
        public Transform Rotation
        {
            get { return m_rotation; }
            set { m_rotation = value; }
        }

        /// <summary>
        /// Current scale transform.
        /// </summary>
        public double Scale
        {
            get { return m_scale; }
            set { m_scale = value; }
        }

        /// <summary>
        /// Project canvas 2D point to the track ball.
        /// </summary>
        /// <param name="width">Canvas width</param>
        /// <param name="height">Canvas height</param>
        /// <param name="point">2D point</param>
        /// <returns>Projected point in track ball</returns>
        private XYZ ProjectToTrackball(double width, double height, Point point)
        {
            double x = point.X / (width / 2);    // Scale so bounds map to [0,0] - [2,2]
            double y = point.Y / (height / 2);

            x = x - 1;                           // Translate 0,0 to the center
            y = 1 - y;                           // Flip so +Y is up instead of down

            double d, t, z;

            d = Math.Sqrt(x * x + y * y);
            if (d < 0.70710678118654752440)
            {    /* Inside sphere */
                z = Math.Sqrt(1 - d * d);
            }
            else
            {           /* On hyperbola */
                t = 1 / 1.41421356237309504880;
                z = t * t / d;
            }
            return new XYZ(x, y, z);
        }

        /// <summary>
        /// Yield the rotation transform according to current 2D point in canvas.
        /// </summary>
        /// <param name="currentPosition">2D point in canvas</param>
        private void Track(Point currentPosition)
        {
            XYZ currentPosition3D = ProjectToTrackball(
                m_canvasWidth, m_canvasHeight, currentPosition);

            XYZ axis = m_previousPosition3D.CrossProduct(currentPosition3D);
            if (axis.GetLength() == 0) return;

            double angle = m_previousPosition3D.AngleTo(currentPosition3D);
            m_rotation = Transform.CreateRotation(axis, -angle);
            m_previousPosition3D = currentPosition3D;
        }

        /// <summary>
        /// Yield the scale transform according to current 2D point in canvas.
        /// </summary>
        /// <param name="currentPosition">2D point in canvas</param>
        private void Zoom(Point currentPosition)
        {
            double yDelta = currentPosition.Y - m_previousPosition2D.Y;

            double scale = Math.Exp(yDelta / 100);    // e^(yDelta/100) is fairly arbitrary.

            m_scale = scale;
        }

        /// <summary>
        /// Mouse down, initialize the transformation to identity.
        /// </summary>
        /// <param name="width">Canvas width</param>
        /// <param name="height">Canvas height</param>
        /// <param name="e"></param>
        public void OnMouseDown(float width, float height, MouseEventArgs e)
        {
            m_rotation = Transform.Identity;
            m_scale = 1.0;
            m_canvasWidth = width;
            m_canvasHeight = height;
            m_previousPosition2D = e.Location;
            m_previousPosition3D = ProjectToTrackball(m_canvasWidth,
                                                     m_canvasHeight,
                                                     m_previousPosition2D);
        }

        /// <summary>
        /// Mouse move with left button press will yield the rotation transform,
        /// with right button press will yield scale transform.
        /// </summary>
        /// <param name="e"></param>
        public void OnMouseMove(MouseEventArgs e)
        {
            Point currentPosition = e.Location;

            // avoid any zero axis conditions
            if (currentPosition == m_previousPosition2D) return;

            // Prefer tracking to zooming if both buttons are pressed.
            if (e.Button == MouseButtons.Left)
            {
                Track(currentPosition);
            }
            else if (e.Button == MouseButtons.Right)
            {
                Zoom(currentPosition);
            }

            m_previousPosition2D = currentPosition;
        }

        /// <summary>
        /// Arrows key down will also yield the rotation transform.
        /// </summary>
        /// <param name="e"></param>
        public void OnKeyDown(KeyEventArgs e)
        {
            XYZ axis = new XYZ(1.0, 0, 0);
            double angle = 0.1;
            switch (e.KeyCode)
            {
                case Keys.Down: break;
                case Keys.Up:
                    angle = -angle;
                    break;
                case Keys.Left:
                    axis = new XYZ(0, 1.0, 0);
                    angle = -angle;
                    break;
                case Keys.Right:
                    axis = new XYZ(0, 1.0, 0);
                    break;
                default: break;
            }
            m_rotation = Transform.CreateRotation(axis, angle);
        }
    }
}