应用程序:ObjectViewer

Revit平台:所有

Revit版本:2011.0

首次发布于:9.0

编程语言:C#

技能等级:高级

类别:视图

类型:ExternalCommand

主题:显示元素的几何形状,获取和设置其参数值。

概要:

这个例子演示了两个主要的特点:

1.如何获取所选元素的物理或分析模型。

2.如何获取和设置所选元素的参数值。

类:

Autodesk.Revit.UI.IExternalCommand

Autodesk.Revit.DB.Element

Autodesk.Revit.DB.GeometryElement

Autodesk.Revit.DB.Parameter

Autodesk.Revit.DB.GeometryObject

Autodesk.Revit.DB.Transform

Autodesk.Revit.DB.Structure.AnalyticalModel

项目文件:

Command.cs

该文件包含Command”类,该类继承自“IExternalCommand”接口并实现“Execute”方法。

 

ErrorMessageException.cs

它包含一个名为ErrorMessageException”的类,用于通过IExternalCommand中的Execute方法将错误消息传递到UI或内部错误消息框。

 

GeometryData.cs

它包含一个名为GeometryData”的类,该类用于获取Revit几何数据并将数据转换为适当的GDI格式。

 

Graphics2DData.cs

它包含:

一个名为Graphics2DData”的类,它是用于图形2D的数据类

一个从Graphics2DData”继承并表示具有未初始化成员数据的PointF类的名为“EmptyGraphics2DData”的类。

 

Graphics3DData.cs

它包含一个名为Graphics3DData”的类,该类是用于图形3D的数据类。

 

MathUtil.cs

它包含一个名为MathUtil”的类,该类提供矩阵算法。

 

ModelData.cs

它包含一个名为ModelData”的类,该类用于从元素的分析模型生成GraphicsData

 

ObjectViewer.cs

它包含ObjectViewer”类,该类是ObjectViewer应用程序的主要命令类。

 

ObjectViewerForm.cs

它包含ObjectViewerForm”类,该类用于在PictureBox中显示所选元素,在DataGridView中显示其参数;还允许通过RadioButtonComboBox等在不同条件下显示元素。

 

Para.cs

它包含一个名为Para”的类,该类用于定义元素的参数。

 

ParasFactory.cs

它包含一个名为ParasFactory”的类,该类用于创建一个“SortableBindingList<Para>”集合。 SortableBindingList可以根据Element存储有关参数的信息。

 

Sketch.cs

它包含一个名为Sketch3D”的类,该类提供在PictureBox中绘制对象的方法并处理操作“Move”“Zoom”“Rotate”。

 

UCS.cs

它包含一个名为UCS”的类,它代表用户坐标系。

 

Vector.cs

它包含一个名为Vector”的类,该类是用于存储点坐标值的Point类。

说明:

本示例演示以下功能:

- 命令仅适用于选择的一个元素。

- 在对话框中,在左侧显示选择的元素的预览窗口,右侧显示通用属性网格。

- 对话框包含用于在物理模型和分析模型之间切换的单选按钮。如果元素没有物理或分析模型,则选项应禁用,并且预览窗口应为灰色。

- 对话框包含用于选择以下意见的列表:

- 物理模型的详细级别。

- 物理模型的视图。

- 用于两个模型(物理模型和分析模型)的视图方向。

- 所选元素的几何形状、物理或分析显示在预览窗格中。元素的通用属性应在网格中显示。

- 预览面板默认为视图方向为-1-1-1,即从一个角度向下看对象。

- 当鼠标位于预览窗口的区域内时,用户可以缩放、旋转和移动模型:

- 单击键‘A’,‘S’,‘D’,‘W’可将模型向左、向下、向右、向上移动。

- 单击‘上箭头’、‘下箭头’、‘左箭头’、‘右箭头’、‘Pg Up’、‘Pg Down’可将模型围绕中心旋转。

- 单击‘Home’和‘End’键可放大和缩小模型。

- 网格中显示的所有参数及其名称、值、单位名称。如果输入值可接受且此参数不是只读的,则可以成功更改参数的值。

 

实现:

- 物理模型可通过元素的Geometry属性获得。

- 分析模型可以在特定元素上可用的AnalyticalModel属性中找到。有几种形式的分析模型都应显示在本示例中。

- 特别注意Inplace Family Instances,以确保它们正确显示。

说明:

1. 绘制一条线、墙、梁或桌子等。

2. 选择其中一个元素,并运行此命令。

3. 将显示一个对话框,其中模式将被呈现在PictureBox中,并在DataGridView中显示其参数,允许通过更改DataGridView中的值来更改参数的值。

4. 选择单选按钮“物理模型”或“分析模型”,以在所选元素的物理模型和分析模型之间切换。

注:如果作为元素没有某些模型,则PictureBox应变为灰色。

5. 如上所述,单击一些键可以移动、旋转或缩放模型,可以从不同的角度看到模型。

6. 改变“详细级别”、“视图方向”下拉框和“显示视图”列表框中的选择将导致PictureBox的转换。它类似于Revit中的操作。

源代码

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

ErrorMessageException.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;

namespace Revit.SDK.Samples.ObjectViewer.CS
{
    /// <summary>
    /// pass error message to UI or back to internal error messagebox by Execute method in IExternalCommand
    /// </summary>
    public class ErrorMessageException : ApplicationException
    {
        /// <summary>
        /// constructor entirely using baseclass'
        /// </summary>
        public ErrorMessageException()
            : base()
        {
        }

        /// <summary>
        /// constructor entirely using baseclass'
        /// </summary>
        /// <param name="message">error message</param>
        public ErrorMessageException(String message)
            : base(message)
        {
        }
    }
}

GeometryData.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.Collections.ObjectModel;

using Autodesk.Revit;
using Autodesk.Revit.DB;

using Element = Autodesk.Revit.DB.Element;
using GeometryElement = Autodesk.Revit.DB.GeometryElement;
using GeometryOptions = Autodesk.Revit.DB.Options;

namespace Revit.SDK.Samples.ObjectViewer.CS
{
   /// <summary>
   /// The GeometryDatafactory object is used to transform Revit geometry data
   /// to appropriate format for GDI.
   /// </summary>
   public class GeometryData
   {
      // boundingBox of the geometry
      private BoundingBoxXYZ m_bbox;
      // curves can represent the wireframe of the geometry
      private List<List<XYZ>> m_curve3Ds = new List<List<XYZ>>();

      /// <summary>
      /// 3D graphics data of the geometry
      /// </summary>
      public Graphics3DData Data3D
      {
         get
         {
            return new Graphics3DData(new List<List<XYZ>>(m_curve3Ds), m_bbox);
         }
      }

      /// <summary>
      /// create 3D and 2D data of given GeometryElement
      /// </summary>
      /// <param name="elem"></param>
      /// <param name="detail"></param>
      /// <param name="currentView"></param>
      public GeometryData(Element elem, ViewDetailLevel detail, View currentView)
      {
         Options opt = Command.CommandData.Application.Application.Create.NewGeometryOptions();
         opt.DetailLevel = detail;
         opt.ComputeReferences = false;
         GeometryElement geoElement = elem.get_Geometry(opt);

         Autodesk.Revit.DB.XYZ xyz = new Autodesk.Revit.DB.XYZ(0, 0, 0);
         Transform transform = Transform.CreateTranslation(xyz);
         AddGeoElement(geoElement, transform);

         m_bbox = elem.get_BoundingBox(currentView);
      }

      /// <summary>
      /// create 3D and 2D data of given GeometryElement
      /// </summary>
      /// <param name="elem">of which geometry data be gotten</param>
      /// <param name="currentView">current view of Revit</param>
      public GeometryData(Element elem, View currentView)
      {
         Options opt = Command.CommandData.Application.Application.Create.NewGeometryOptions();
         opt.View = currentView;
         opt.ComputeReferences = false;
         GeometryElement geoElement = elem.get_Geometry(opt);

         Autodesk.Revit.DB.XYZ xyz = new Autodesk.Revit.DB.XYZ(0, 0, 0);
         Transform transform = Transform.CreateTranslation(xyz);
         AddGeoElement(geoElement, transform);

         m_bbox = elem.get_BoundingBox(currentView);
      }

      /// <summary>
      /// get the solids in a Geometric primitive
      /// </summary>
      /// <param name="obj">a geometry object of element</param>
      /// <param name="transform"></param>
      private void AddGeoElement(GeometryObject obj, Transform transform)
      {
         GeometryElement geometry = obj as GeometryElement;
         if (null == geometry)
         {
            return;
         }

         //get all geometric primitives contained in the GeometryElement
         IEnumerator<GeometryObject> Objects = geometry.GetEnumerator();

         AddGeometryObjects(Objects, transform);
      }

      /// <summary>
      /// iterate GeometryObject in GeometryObjectArray and generate data accordingly
      /// </summary>
      /// <param name="objects"></param>
      /// <param name="transform"></param>
      private void AddGeometryObjects(IEnumerator<GeometryObject> objects, Transform transform)
      {
         //foreach (GeometryObject o in objects)
         while (objects.MoveNext())
         {
            GeometryObject o = objects.Current;

            //if the type of the geometric primitive is Solid
            string geoType = o.GetType().Name;
            switch (geoType)
            {
               case "Solid":
                  AddSolid(o, transform);
                  break;
               case "Face":
                  AddFace(o, transform);
                  break;
               case "Mesh":
                  AddMesh(o, transform);
                  break;
               case "Curve":
               case "Line":
               case "Arc":
                  AddCurve(o, transform);
                  break;
               case "Profile":
                  AddProfile(o, transform);
                  break;
               case "Element":
                  AddGeoElement(o, transform);
                  break;
               case "Instance":
                  AddInstance(o, transform);
                  break;
               case "Edge":
                  AddEdge(o, transform);
                  break;
               default:
                  break;
            }
         }
      }

      /// <summary>
      /// generate data of a Solid
      /// </summary>
      /// <param name="obj"></param>
      /// <param name="transform"></param>
      private void AddSolid(GeometryObject obj, Transform transform)
      {
         Solid solid = obj as Solid;
         if (null == solid)
         {
            return;
         }

         //a solid has many faces
         FaceArray faces = solid.Faces;
         if (faces.Size == 0)
         {
            return;
         }

         foreach (Face face in faces)
         {
            AddFace(face, transform);
         }
      }

      /// <summary>
      /// generate data of a Face
      /// </summary>
      /// <param name="obj"></param>
      /// <param name="transform"></param>
      private void AddFace(GeometryObject obj, Transform transform)
      {
         Face face = obj as Face;
         if (null == face)
         {
            return;
         }

         Mesh mesh = face.Triangulate();
         if (null == mesh)
         {
            return;
         }
         AddMesh(mesh, transform);
      }

      /// <summary>
      /// generate data of a Profile
      /// </summary>
      /// <param name="obj"></param>
      /// <param name="transform"></param>
      private void AddProfile(GeometryObject obj, Transform transform)
      {
         Profile profile = obj as Profile;
         if (null == profile)
         {
            return;
         }

         foreach (Curve curve in profile.Curves)
         {
            AddCurve(curve, transform);
         }
      }

      /// <summary>
      /// generate data of a Mesh
      /// </summary>
      /// <param name="obj"></param>
      /// <param name="transform"></param>
      private void AddMesh(GeometryObject obj, Transform transform)
      {
         Mesh mesh = obj as Mesh;
         if (null == mesh)
         {
            return;
         }

         //a face has a mesh, all meshes are made of triangles
         for (int i = 0; i < mesh.NumTriangles; i++)
         {
            MeshTriangle triangular = mesh.get_Triangle(i);
            List<XYZ> points = new List<XYZ>();
            try
            {
               for (int n = 0; n < 3; n++)
               {
                  Autodesk.Revit.DB.XYZ point = triangular.get_Vertex(n);
                  Autodesk.Revit.DB.XYZ newPoint = MathUtil.GetBasis(point, transform);
                  points.Add(newPoint);
               }
               Autodesk.Revit.DB.XYZ iniPoint = points[0];
               points.Add(iniPoint);
               m_curve3Ds.Add(points);
            }
            catch
            {
            }
         }
      }

      /// <summary>
      /// generate data of a Curve
      /// </summary>
      /// <param name="obj"></param>
      /// <param name="transform"></param>
      private void AddCurve(GeometryObject obj, Transform transform)
      {
         Curve curve = obj as Curve;
         if (null == curve)
         {
            return;
         }

         if (curve.IsBound)
         {
            List<XYZ> points = curve.Tessellate() as List<XYZ>;
            List<XYZ> result = new List<XYZ>();
            foreach (Autodesk.Revit.DB.XYZ point in points)
            {
               Autodesk.Revit.DB.XYZ newPoint = MathUtil.GetBasis(point, transform);
               result.Add(newPoint);
            }
            m_curve3Ds.Add(result);
         }
      }

      /// <summary>
      /// generate data of a Instance
      /// </summary>
      /// <param name="obj"></param>
      /// <param name="transform"></param>
      private void AddInstance(GeometryObject obj, Transform transform)
      {
         GeometryInstance instance = obj as GeometryInstance;
         if (null == instance)
         {
            return;
         }
         //get a transformation of the affine 3-space
         Transform allTransform = AddTransform(transform, instance.Transform);

         GeometryElement instanceGeometry = instance.SymbolGeometry;
         if (null == instanceGeometry)
         {
            return;
         }
         //get all geometric primitives contained in the GeometryElement
         //GeometryObjectArray instanceGeometries = instanceGeometry.Objects;
         IEnumerator<GeometryObject> Objects = instanceGeometry.GetEnumerator();

         AddGeometryObjects(Objects, allTransform);
      }

      /// <summary>
      /// generate data of a Edge
      /// </summary>
      /// <param name="obj"></param>
      /// <param name="transform"></param>
      private void AddEdge(GeometryObject obj, Transform transform)
      {
         Edge edge = obj as Edge;
         if (null == edge)
         {
            return;
         }

         List<XYZ> points = edge.Tessellate() as List<XYZ>;
         List<XYZ> result = new List<XYZ>();
         foreach (Autodesk.Revit.DB.XYZ point in points)
         {
            Autodesk.Revit.DB.XYZ newPoint = MathUtil.GetBasis(point, transform);
            result.Add(newPoint);
         }
         m_curve3Ds.Add(result);
      }

      /// <summary>
      /// Add 2 Transform Matrix
      /// </summary>
      /// <param name="tran1"></param>
      /// <param name="tran2"></param>
      /// <returns></returns>
      private Transform AddTransform(Transform tran1, Transform tran2)
      {
         Autodesk.Revit.DB.XYZ xyz = new Autodesk.Revit.DB.XYZ(0, 0, 0);
         Transform result = Transform.CreateTranslation(xyz);
         result.Origin = MathUtil.AddXYZ(tran1.Origin, tran2.Origin);

         Autodesk.Revit.DB.XYZ[] left = new Autodesk.Revit.DB.XYZ[3];
         Autodesk.Revit.DB.XYZ[] right = new Autodesk.Revit.DB.XYZ[3];

         for (int i = 0; i < 3; i++)
         {
            left[i] = tran1.get_Basis(i);
            right[i] = tran2.get_Basis(i);
         }

         Autodesk.Revit.DB.XYZ[] temp = MathUtil.MultiCross(left, right);

         for (int i = 0; i < 3; i++)
         {
            result.set_Basis(i, temp[i]);
         }

         return result;
      }
   }
}

Graphics2DData.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 System.Collections.ObjectModel;

using Autodesk.Revit.DB;

namespace Revit.SDK.Samples.ObjectViewer.CS
{
    /// <summary>
    /// data class for graphics 2D
    /// </summary>
    public class Graphics2DData
    {
        // curves represent the wireframe of the 2D geometry
        private readonly List<PointF[]> m_curves = new List<PointF[]>();
        // the number of points consists of m_curves
        private readonly int m_numPoints;
        private static EmptyGraphics2DData m_empty = new EmptyGraphics2DData();

        // boundingbox of the 2D geometry
        private RectangleF m_bbox = new RectangleF(0.0f, 0.0f, 1.0f, 1.0f);

        /// <summary>
        /// Represents a new instance of the Graphics2DData class with member data left uninitialized
        /// </summary>
        public static Graphics2DData Empty
        {
            get
            {
                return m_empty;
            }
        }

        /// <summary>
        /// Number of points consists of 2D geometry's wireframe
        /// </summary>
        public int NumPoints
        {
            get
            {
                return m_numPoints;
            }
        }

        /// <summary>
        /// BoundingBox of the 2D geometry
        /// </summary>
        public RectangleF BBox
        {
            get
            {
                return m_bbox;
            }
        }

        /// <summary>
        /// Curves represent the wireframe of the 2D geometry
        /// </summary>
        public List<PointF[]> ConstCurves
        {
            get
            {
                return m_curves;
            }
        }

        /// <summary>
        /// only for Empty class
        /// </summary>
        protected Graphics2DData()
        {
        }

        /// <summary>
        /// constructor
        /// </summary>
        /// <param name="curves">curves represent the wireframe of the 2D geometry</param>
        public Graphics2DData(List<List<UV>> curves)
        {
            // initialize m_curves and m_bbox
            bool isFirst = true;    // is the first List<UV> instance in curves
            foreach (List<UV> uvs in curves)
            {
                PointF[] pnts = new PointF[uvs.Count];
                m_curves.Add(pnts);
                for (int i = 0; i < uvs.Count; i++)
                {
                    Autodesk.Revit.DB.UV uv = uvs[i];
                    pnts[i] = new PointF((float)uv.U, (float)uv.V);
                    RectangleF tmpBBox = new RectangleF(pnts[i], new SizeF(0.0f, 0.0f));
                    m_numPoints++;
                    if (!isFirst)
                    {
                        // union the m_bbox with next curve's BBox
                        m_bbox = RectangleF.Union(m_bbox, tmpBBox);
                    }
                    else
                    {
                        m_bbox = tmpBBox;
                        isFirst = false;
                    }
                }
            }
        }

        /// <summary>
        /// Represents a PointF class with member data left uninitialized
        /// </summary>
        class EmptyGraphics2DData : Graphics2DData
        {
        }
    }
}

Graphics3DData.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 System.Drawing.Drawing2D;
using System.Collections.ObjectModel;

using Autodesk.Revit.DB.Structure;
using Autodesk.Revit.DB;

namespace Revit.SDK.Samples.ObjectViewer.CS
{
    /// <summary>
    /// trigger when view data updated
    /// </summary>
    public delegate void UpdateViewDelegate();

    /// <summary>
    /// data class for graphics 3D
    /// </summary>
    public class Graphics3DData
    {
        /// <summary>
        /// Chooses the camera direction to the view
        /// </summary>
        public enum ViewDirections
        {
            /// <summary>
            /// top direction
            /// </summary>
            Top,

            /// <summary>
            /// front view
            /// </summary>
            Front,

            /// <summary>
            /// left view
            /// </summary>
            Left,

            /// <summary>
            /// right view
            /// </summary>
            Right,

            /// <summary>
            /// bottom view
            /// </summary>
            Bottom,

            /// <summary>
            /// back view
            /// </summary>
            Back,

            /// <summary>
            /// iso view diection
            /// </summary>
            IsoMetric
        }

        // default angle when rotate around X,Y,Z axis
        private const double RotateAngle = System.Math.PI / 90;
        // initial curves data before UCS transform
        private readonly List<List<Vector>> m_iniCurves = new List<List<Vector>>();
        // curves data after UCS transform
        private readonly List<List<Vector>> m_curves = new List<List<Vector>>();
        // number of points representing the 3D geometry
        private readonly int m_numPoints;

        // BoundingBox of the 3D geometry
        private BoundingBoxXYZ m_bbox = new BoundingBoxXYZ();
        // current UCS
        private UCS m_currentUCS = new UCS();

        /// <summary>
        /// Current UCS used to transform
        /// </summary>
        public UCS CurrentUCS
        {
            get
            {
                return m_currentUCS;
            }
            set
            {
                m_currentUCS = value;
                UpdateDisplayData(m_currentUCS);
                if (null != UpdateViewEvent)
                {
                    UpdateViewEvent();
                }
            }
        }

        /// <summary>
        /// Number of points representing the 3D geometry
        /// </summary>
        public int NumPoints
        {
            get
            {
                return m_numPoints;
            }
        }

        // 7 UCS according to camera directions in ViewDirections
        private static readonly UCS[] SpecialUCSs = new UCS[7];
        // small angle used to Rotate UCS around X, Y, Z axis
        private static readonly UCS RotateXUCS;
        private static readonly UCS RotateAntiXUCS;
        private static readonly UCS RotateYUCS;
        private static readonly UCS RotateAntiYUCS;
        private static readonly UCS RotateZUCS;
        private static readonly UCS RotateAntiZUCS;

        /// <summary>
        /// static constructor used to initialize static members
        /// </summary>
        static Graphics3DData()
        {
            // initialize small angle
            double angle = RotateAngle;
            double antiAngle = -RotateAngle;
            double sin = Math.Sin(angle);
            double cos = Math.Cos(angle);
            double antiSin = Math.Sin(antiAngle);
            double antiCos = Math.Cos(antiAngle);
            // initialize 6 Rotate UCS
            Vector origin = new Vector();
            RotateXUCS =
                new UCS(origin, new Vector(1.0, 0.0, 0.0), new Vector(0.0, cos, sin));
            RotateAntiXUCS =
                new UCS(origin, new Vector(1.0, 0.0, 0.0), new Vector(0.0, antiCos, antiSin));
            RotateYUCS =
                new UCS(origin, new Vector(cos, 0.0, -sin), new Vector(0.0, 1.0, 0.0));
            RotateAntiYUCS =
                new UCS(origin, new Vector(antiCos, 0.0, -antiSin), new Vector(0.0, 1.0, 0.0));
            RotateZUCS =
                new UCS(origin, new Vector(cos, sin, 0.0), new Vector(-sin, cos, 0.0));
            RotateAntiZUCS =
                new UCS(origin, new Vector(antiCos, antiSin, 0.0), new Vector(-antiSin, antiCos, 0.0));

            // initialize 7 special UCS
            SpecialUCSs[(int)ViewDirections.IsoMetric] = new UCS(origin,
                new Vector(-0.408248290463863, 0.408248290463863, 0.816496580927726),
                new Vector(0.707106781186548, 0.707106781186548, 0.0));
            SpecialUCSs[(int)ViewDirections.Top] =
                new UCS(origin, new Vector(1.0, 0.0, 0.0), new Vector(0.0, 1.0, 0.0));
            SpecialUCSs[(int)ViewDirections.Front] =
                new UCS(origin, new Vector(-1.0, 0.0, 0.0), new Vector(0.0, 0.0, 1.0));
            SpecialUCSs[(int)ViewDirections.Left] =
                new UCS(origin, new Vector(0.0, -1.0, 0.0), new Vector(0.0, 0.0, 1.0));
            SpecialUCSs[(int)ViewDirections.Right] =
                new UCS(origin, new Vector(0.0, 1.0, 0.0), new Vector(0.0, 0.0, 1.0));
            SpecialUCSs[(int)ViewDirections.Bottom] =
                new UCS(origin, new Vector(-1.0, 0.0, 0.0), new Vector(0.0, 1.0, 0.0));
            SpecialUCSs[(int)ViewDirections.Back] =
                new UCS(origin, new Vector(1.0, 0.0, 0.0), new Vector(0.0, 0.0, 1.0));
        }

        /// <summary>
        /// view data update
        /// </summary>
        public event UpdateViewDelegate UpdateViewEvent;

        /// <summary>
        /// Curves represent the wireframe of the 3D geometry
        /// </summary>
        public List<List<Vector>> ConstCurves
        {
            get
            {
                return m_curves;
            }
        }

        /// <summary>
        /// BoundingBox of the 3D geometry
        /// </summary>
        public BoundingBoxXYZ BBox
        {
            get
            {
                return m_bbox;
            }
        }

        /// <summary>
        /// constructor
        /// </summary>
        /// <param name="curves">curves represent the wireframe of the 3D geometry</param>
        /// <param name="bbox">oundingBox of the 3D geometry</param>
        public Graphics3DData(List<List<XYZ>> curves, BoundingBoxXYZ bbox)
        {
            if (null == bbox)
            {
                return;
            }

            foreach (UCS ucs in SpecialUCSs)
            {
                ucs.Origin = FindBBoxCenter(bbox);
            }

            foreach (List<XYZ> pnts in curves)
            {
                List<Vector> vectors = new List<Vector>();
                m_iniCurves.Add(vectors);
                foreach (Autodesk.Revit.DB.XYZ pnt in pnts)
                {
                    vectors.Add(new Vector(pnt.X, pnt.Y, pnt.Z));
                    m_numPoints++;
                }
            }
            SetViewDirection(ViewDirections.IsoMetric);
            InitializeBBox(bbox);
        }

        /// <summary>
        /// change the camera direction to the geometry
        /// </summary>
        /// <param name="viewDirection">camera direction</param>
        public void SetViewDirection(ViewDirections viewDirection)
        {
            m_currentUCS = SpecialUCSs[(int)viewDirection];
            UpdateDisplayData(m_currentUCS);
        }

        /// <summary>
        /// rotate around Z axis with default angle
        /// </summary>
        /// <param name="direction">minus or positive angle</param>
        public void RotateZ(bool direction)
        {
            if (direction)
            {
                m_currentUCS = m_currentUCS + RotateZUCS;
                UpdateDisplayData(m_currentUCS);
            }
            else
            {
                m_currentUCS = m_currentUCS + RotateAntiZUCS;
                UpdateDisplayData(m_currentUCS);
            }
        }

        /// <summary>
        /// rotate around Y axis with default angle
        /// </summary>
        /// <param name="direction">minus or positive angle</param>
        public void RotateY(bool direction)
        {
            if (direction)
            {
                m_currentUCS = m_currentUCS + RotateYUCS;
                UpdateDisplayData(m_currentUCS);
            }
            else
            {
                m_currentUCS = m_currentUCS + RotateAntiYUCS;
                UpdateDisplayData(m_currentUCS);
            }
        }

        /// <summary>
        /// rotate around X axis with default angle
        /// </summary>
        /// <param name="direction">minus or positive angle</param>
        public void RotateX(bool direction)
        {
            if (direction)
            {
                m_currentUCS = m_currentUCS + RotateXUCS;
                UpdateDisplayData(m_currentUCS);
            }
            else
            {
                m_currentUCS = m_currentUCS + RotateAntiXUCS;
                UpdateDisplayData(m_currentUCS);
            }
        }

        /// <summary>
        /// update data according to current UCS
        /// </summary>
        /// <param name="lc"></param>
        private void UpdateDisplayData(UCS lc)
        {
            m_curves.Clear();
            foreach (List<Vector> iniVectors in m_iniCurves)
            {
                List<Vector> vectors = new List<Vector>();
                m_curves.Add(vectors);
                for (int i = 0; i < iniVectors.Count; i++)
                {
                    // transform points to local coordinate system
                    vectors.Add(lc.GC2LC(iniVectors[i]));
                }
            }
            // trigger update view event
            if (null != UpdateViewEvent)
            {
                UpdateViewEvent();
            }
        }

        /// <summary>
        /// move the BoundingBox to the center of the Coordinate System,
        /// modify its size to the size of Geometry's BoundingBox
        /// </summary>
        /// <param name="bbox"></param>
        private void InitializeBBox(BoundingBoxXYZ bbox)
        {
            Autodesk.Revit.DB.XYZ size = MathUtil.SubXYZ(bbox.Max, bbox.Min);
            m_bbox.Max = MathUtil.DivideXYZ(size, 2.0);
            m_bbox.Min = MathUtil.DivideXYZ(size, -2.0);
        }

        /// <summary>
        /// find the center of the BoundingBox
        /// </summary>
        /// <param name="bbox">BoundingBox</param>
        /// <returns>center Point</returns>
        private Vector FindBBoxCenter(BoundingBoxXYZ bbox)
        {
            Autodesk.Revit.DB.XYZ center = MathUtil.DivideXYZ(MathUtil.AddXYZ(bbox.Max, bbox.Min), 2.0);
            return MathUtil.XYZ2Vector(center);
        }
    }
}

MathUtil.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 System.Drawing.Drawing2D;

using Autodesk.Revit.DB.Structure;
using Autodesk.Revit.DB;

namespace Revit.SDK.Samples.ObjectViewer.CS
{
    /// <summary>
    /// utility class provide arithmetic of matrix
    /// </summary>
    public static class MathUtil
    {
        /// <summary>
        /// multiply cross two matrix
        /// </summary>
        /// <param name="m1">left matrix</param>
        /// <param name="m2">right matrix</param>
        /// <returns>result matrix</returns>
        public static Autodesk.Revit.DB.XYZ[] MultiCross(Autodesk.Revit.DB.XYZ[] m1, Autodesk.Revit.DB.XYZ[] m2)
        {
            Autodesk.Revit.DB.XYZ[] result = new Autodesk.Revit.DB.XYZ[3];

            for (int i = 0; i < 3; i++)
            {
                result[i] = new Autodesk.Revit.DB.XYZ();
            }

            for (int i = 0; i < 3; i++)
            {
                for (int j = 0; j < 3; j++)
                {
                    for (int k = 0; k < 3; k++)
                    {
                        switch (j)
                        {
                            case 0:
                                result[i] = new XYZ(
                                    result[i].X + ((m1[i])[k] * (m2[k])[j]),
                                    result[i].Y,
                                    result[i].Z);
                                break;
                            case 1:
                                result[i] = new XYZ(
                                    result[i].X,
                                    result[i].Y + (m1[i])[k] * (m2[k])[j],
                                    result[i].Z);
                                break;
                            case 2:
                                result[i] = new XYZ(
                                    result[i].X,
                                    result[i].Y,
                                    result[i].Z + (m1[i])[k] * (m2[k])[j]);
                                break;
                            default:
                                break;
                        }
                    }
                }
            }

            return result;
        }

        /// <summary>
        /// subtract two Autodesk.Revit.DB.XYZ as Matrix
        /// </summary>
        /// <param name="lhs"></param>
        /// <param name="rhs"></param>
        /// <returns></returns>
        public static Autodesk.Revit.DB.XYZ SubXYZ(Autodesk.Revit.DB.XYZ lhs, Autodesk.Revit.DB.XYZ rhs)
        {
            double x = lhs.X - rhs.X;
            double y = lhs.Y - rhs.Y;
            double z = lhs.Z - rhs.Z;

            Autodesk.Revit.DB.XYZ result = new Autodesk.Revit.DB.XYZ (x, y, z);
            return result;
        }

        /// <summary>
        /// Add two Autodesk.Revit.DB.XYZ as Matrix
        /// </summary>
        /// <param name="lhs"></param>
        /// <param name="rhs"></param>
        /// <returns></returns>
        public static Autodesk.Revit.DB.XYZ AddXYZ(Autodesk.Revit.DB.XYZ lhs, Autodesk.Revit.DB.XYZ rhs)
        {
            double x = lhs.X + rhs.X;
            double y = lhs.Y + rhs.Y;
            double z = lhs.Z + rhs.Z;

            Autodesk.Revit.DB.XYZ result = new Autodesk.Revit.DB.XYZ (x, y, z);
            return result;
        }

        /// <summary>
        /// divide a Autodesk.Revit.DB.XYZ by a number
        /// </summary>
        /// <param name="lhs">divided XYZ</param>
        /// <param name="rhs">number</param>
        /// <returns>result</returns>
        public static Autodesk.Revit.DB.XYZ DivideXYZ(Autodesk.Revit.DB.XYZ lhs, double rhs)
        {
            return new Autodesk.Revit.DB.XYZ (lhs.X / rhs, lhs.Y / rhs, lhs.Z / rhs);
        }

        /// <summary>
        /// get the coordinates from old coordinate system in the new coordinate system
        /// </summary>
        /// <param name="point"></param>
        /// <param name="transform"></param>
        /// <returns></returns>
        public static Autodesk.Revit.DB.XYZ GetBasis(Autodesk.Revit.DB.XYZ point, Transform transform)
        {
            double x = point.X;
            double y = point.Y;
            double z = point.Z;
            double x2;
            double y2;
            double z2;

            //transform basis of the old coordinate system in the new coordinate system
            Autodesk.Revit.DB.XYZ b0 = transform.get_Basis(0);
            Autodesk.Revit.DB.XYZ b1 = transform.get_Basis(1);
            Autodesk.Revit.DB.XYZ b2 = transform.get_Basis(2);
            Autodesk.Revit.DB.XYZ origin = transform.Origin;

            //transform the origin of the old coordinate system in the new coordinate system
            x2 = x * b0.X + y * b1.X + z * b2.X + origin.X;
            y2 = x * b0.Y + y * b1.Y + z * b2.Y + origin.Y;
            z2 = x * b0.Z + y * b1.Z + z * b2.Z + origin.Z;

            Autodesk.Revit.DB.XYZ newPoint = new Autodesk.Revit.DB.XYZ (x2, y2, z2);
            return newPoint;
        }

        /// <summary>
        /// transform a Vector instance to a Autodesk.Revit.DB.XYZ instance
        /// </summary>
        /// <param name="v">transformed Vector</param>
        /// <returns>result XYZ</returns>
        public static Autodesk.Revit.DB.XYZ Vector2XYZ(Vector v)
        {
            return new Autodesk.Revit.DB.XYZ (v.X, v.Y, v.Z);
        }

        /// <summary>
        /// transform a Autodesk.Revit.DB.XYZ instance to a Vector instance
        /// </summary>
        /// <param name="pnt">transformed XYZ</param>
        /// <returns>result Vector</returns>
        public static Vector XYZ2Vector(Autodesk.Revit.DB.XYZ pnt)
        {
            return new Vector(pnt.X, pnt.Y, pnt.Z);
        }
    }
}

ModelData.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 System.Drawing.Drawing2D;
using System.Collections.ObjectModel;

using Autodesk.Revit;
using Autodesk.Revit.DB;
using Autodesk.Revit.DB.Structure;

using Element = Autodesk.Revit.DB.Element;
using GeomElement = Autodesk.Revit.DB.GeometryElement;
using GeomInstance = Autodesk.Revit.DB.Instance;

namespace Revit.SDK.Samples.ObjectViewer.CS
{
    /// <summary>
    /// generate GraphicsData by give geometry object
    /// </summary>
    public class ModelData
    {
        // BoundingBox of the geometry
        private BoundingBoxXYZ m_bbox;
        private List<List<XYZ>> m_curve3Ds = new List<List<XYZ>>();
        private List<List<UV>> m_curve2Ds = new List<List<UV>>();

        /// <summary>
        /// 3D graphics data of the geometry
        /// </summary>
        public Graphics3DData Data3D
        {
            get
            {
                return new Graphics3DData(new List<List<XYZ>>(m_curve3Ds), m_bbox);
            }
        }

        /// <summary>
        /// 2D graphics data of the geometry
        /// </summary>
        public Graphics2DData Data2D
        {
            get
            {
                return new Graphics2DData(new List<List<UV>>(m_curve2Ds));
            }
        }

        /// <summary>
        /// Generate appropriate graphics data object from exact analytical model type.
        /// </summary>
        /// <param name="element">The selected element maybe has analytical model lines</param>
        /// <returns>A graphics data object appropriate for GDI.</returns>
        public ModelData(Element element)
        {
            try
            {
                AnalyticalModel analyticalMode = GetAnalyticalModel(element);
                View currentView = Command.CommandData.Application.ActiveUIDocument.Document.ActiveView;
                m_bbox = element.get_BoundingBox(currentView);

                if (null == analyticalMode)
                {
                    return;
                }

                GetModelData(analyticalMode);

                
            }
            catch (Exception)
            {
            }
        }

        /// <summary>
        /// Get the analytical model object from an element.
        /// </summary>
        /// <param name="element">The selected element maybe has analytical model lines</param>
        /// <returns>Return analytical model object, or else return null.</returns>
        private AnalyticalModel GetAnalyticalModel(Element element)
        {
            return element.GetAnalyticalModel();
        }

        /// <summary>
        /// create GraphicsData of give AnalyticalModel
        /// </summary>
        /// <param name="model">AnalyticalModel contains geometry data</param>
        /// <returns>A graphics data object appropriate for GDI.</returns>
        private void GetModelData(AnalyticalModel model)
        {
            foreach (Curve curve in model.GetCurves(AnalyticalCurveType.RawCurves))
            {
                try
                {
                    m_curve3Ds.Add(curve.Tessellate() as List<XYZ>);
                }
                catch
                {
                }
            }
        }
    }
}

ObjectViewer.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.ObjectViewer.CS
{
    using System;
    using System.Collections.Generic;
    using System.Collections.ObjectModel;
    using System.ComponentModel;

    using Autodesk.Revit;
    using Autodesk.Revit.DB;
    using Autodesk.Revit.UI;
    using Autodesk.Revit.DB.Structure;

    using Element = Autodesk.Revit.DB.Element;
    using GeometryElement = Autodesk.Revit.DB.GeometryElement;

    /// <summary>
    /// main command class of the ObjectViewer application
    /// </summary>
    public class ObjectViewer
    {
        /// <summary>
        /// choose the display kinds of preview
        /// </summary>
        public enum DisplayKinds
        {
            /// <summary>
            /// Geometry Model
            /// </summary>
            GeometryModel,

            /// <summary>
            /// AnalyticalModel
            /// </summary>
            AnalyticalModel
        }

        private Element m_selected;     // selected element to display
        private View m_currentView;     // current View for preview
        // current detail level for preview
        private ViewDetailLevel m_detailLevel = ViewDetailLevel.Fine;
        private List<View> m_allViews = new List<View>();   // all Views in current project
        // current display kind for preview
        private DisplayKinds m_displayKind = DisplayKinds.GeometryModel;
        // selected element's parameters' data
        private SortableBindingList<Para> m_paras;
        // current sketch instance to draw the element
        private Sketch3D m_currentSketch3D = null;
        // indicate whether select view or detail
        private bool m_isSelectView = true;

        /// <summary>
        /// Current display kind for preview
        /// </summary>
        public DisplayKinds DisplayKind
        {
            get
            {
                return m_displayKind;
            }
            set
            {
                m_displayKind = value;
                UpdateSketch3D();
            }
        }

        /// <summary>
        /// Current View for preview
        /// </summary>
        public View CurrentView
        {
            get
            {
                return m_currentView;
            }
            set
            {
                m_currentView = value;
                m_isSelectView = true;
                UpdateSketch3D();
            }
        }

        /// <summary>
        /// All Views in current project
        /// </summary>
        public ReadOnlyCollection<View> AllViews
        {
            get
            {
                return new ReadOnlyCollection<View>(m_allViews);
            }
        }

        /// <summary>
        /// Current detail level for preview
        /// </summary>
        public ViewDetailLevel DetailLevel
        {
            get
            {
                return m_detailLevel;
            }
            set
            {
                m_detailLevel = value;
                m_isSelectView = false;
                UpdateSketch3D();
            }
        }

        /// <summary>
        /// Current sketch instance to draw the element
        /// </summary>
        public Sketch3D CurrentSketch3D
        {
            get
            {
                return m_currentSketch3D;
            }
        }

        /// <summary>
        /// Selected element's parameters' data
        /// </summary>
        public SortableBindingList<Para> Params
        {
            get
            {
                return m_paras;
            }
        }

        /// <summary>
        /// constructor
        /// </summary>
        public ObjectViewer()
        {
            UIDocument doc = Command.CommandData.Application.ActiveUIDocument;
            ElementSet selection = new ElementSet();
            foreach (ElementId elementId in doc.Selection.GetElementIds())
            {
               selection.Insert(doc.Document.GetElement(elementId));
            }
            // only one element should be selected
            if (0 == selection.Size)
            {
                throw new ErrorMessageException("Please select an element.");
            }

            if (1 < selection.Size)
            {
                throw new ErrorMessageException("Please select only one element.");
            }
            // get selected element
            foreach (Element e in selection)
            {
                m_selected = e;
            }
            // get current view and all views
            m_currentView = doc.Document.ActiveView;
            FilteredElementIterator itor = (new FilteredElementCollector(doc.Document)).OfClass(typeof(View)).GetElementIterator();
            itor.Reset();
            while (itor.MoveNext())
            {
                View view = itor.Current as View;
                // Skip view templates because they're invisible in project browser, invalid for geometry elements
                if (null != view && !view.IsTemplate)
                {
                    m_allViews.Add(view);
                }
            }

            // create a instance of Sketch3D
            GeometryData geomFactory = new GeometryData(m_selected, m_currentView);
            m_currentSketch3D = new Sketch3D(geomFactory.Data3D, Graphics2DData.Empty);

            //get a instance of ParametersFactory and then use it to create Parameters
            ParasFactory parasFactory = new ParasFactory(m_selected);
            m_paras = parasFactory.CreateParas();
        }

        /// <summary>
        /// update current Sketch3D using current UCS and settings
        /// </summary>
        private void UpdateSketch3D()
        {
            if (m_displayKind == DisplayKinds.GeometryModel)
            {
                GeometryData geomFactory;
                if(m_isSelectView)
                {
                    geomFactory = new GeometryData(m_selected, m_currentView);
                    m_detailLevel = ViewDetailLevel.Undefined;

                }
                else
                {
                    geomFactory = new GeometryData(m_selected, m_detailLevel, m_currentView);
                }

                Graphics3DData geom3DData = geomFactory.Data3D;
                Graphics3DData old3DData = m_currentSketch3D.Data3D;
                geom3DData.CurrentUCS = old3DData.CurrentUCS;
                m_currentSketch3D.Data3D = geom3DData;
                m_currentSketch3D.Data2D = Graphics2DData.Empty;
            }
            else if (m_displayKind == DisplayKinds.AnalyticalModel)
            {
                ModelData modelFactory = new ModelData(m_selected);
                Graphics3DData model3DData = modelFactory.Data3D;
                Graphics2DData model2DData = modelFactory.Data2D;
                Graphics3DData old3DData = m_currentSketch3D.Data3D;
                model3DData.CurrentUCS = old3DData.CurrentUCS;
                m_currentSketch3D.Data3D = model3DData;
                m_currentSketch3D.Data2D = model2DData;
            }
        }
    }
}

ObjectViewerForm.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.ObjectViewer.CS
{
    partial class ObjectViewerForm
    {
        /// <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.parametersDataGridView = new System.Windows.Forms.DataGridView();
            this.physicalModelRadioButton = new System.Windows.Forms.RadioButton();
            this.analyticalModelRadioButton = new System.Windows.Forms.RadioButton();
            this.previewBox = new System.Windows.Forms.PictureBox();
            this.groupBox1 = new System.Windows.Forms.GroupBox();
            this.label5 = new System.Windows.Forms.Label();
            this.viewDirectionComboBox = new System.Windows.Forms.ComboBox();
            this.label4 = new System.Windows.Forms.Label();
            this.viewListBox = new System.Windows.Forms.ListBox();
            this.label1 = new System.Windows.Forms.Label();
            this.detailLevelComboBox = new System.Windows.Forms.ComboBox();
            this.label2 = new System.Windows.Forms.Label();
            this.label3 = new System.Windows.Forms.Label();
            this.okButton = new System.Windows.Forms.Button();
            this.closeButton = new System.Windows.Forms.Button();
            ((System.ComponentModel.ISupportInitialize)(this.parametersDataGridView)).BeginInit();
            ((System.ComponentModel.ISupportInitialize)(this.previewBox)).BeginInit();
            this.groupBox1.SuspendLayout();
            this.SuspendLayout();
            //
            // parametersDataGridView
            //
            this.parametersDataGridView.AllowUserToAddRows = false;
            this.parametersDataGridView.AllowUserToDeleteRows = false;
            this.parametersDataGridView.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
                        | System.Windows.Forms.AnchorStyles.Right)));
            this.parametersDataGridView.BorderStyle = System.Windows.Forms.BorderStyle.Fixed3D;
            this.parametersDataGridView.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize;
            this.parametersDataGridView.Location = new System.Drawing.Point(490, 25);
            this.parametersDataGridView.Name = "parametersDataGridView";
            this.parametersDataGridView.RowHeadersVisible = false;
            this.parametersDataGridView.RowTemplate.Height = 24;
            this.parametersDataGridView.Size = new System.Drawing.Size(404, 567);
            this.parametersDataGridView.TabIndex = 10;
            this.parametersDataGridView.TabStop = false;
            //
            // physicalModelRadioButton
            //
            this.physicalModelRadioButton.AutoSize = true;
            this.physicalModelRadioButton.Checked = true;
            this.physicalModelRadioButton.Location = new System.Drawing.Point(6, 110);
            this.physicalModelRadioButton.Name = "physicalModelRadioButton";
            this.physicalModelRadioButton.Size = new System.Drawing.Size(96, 17);
            this.physicalModelRadioButton.TabIndex = 8;
            this.physicalModelRadioButton.TabStop = true;
            this.physicalModelRadioButton.Text = "Physical Model";
            this.physicalModelRadioButton.UseVisualStyleBackColor = true;
            this.physicalModelRadioButton.CheckedChanged += new System.EventHandler(this.physicalModelRadioButton_CheckedChanged);
            //
            // analyticalModelRadioButton
            //
            this.analyticalModelRadioButton.AutoSize = true;
            this.analyticalModelRadioButton.Location = new System.Drawing.Point(6, 133);
            this.analyticalModelRadioButton.Name = "analyticalModelRadioButton";
            this.analyticalModelRadioButton.Size = new System.Drawing.Size(102, 17);
            this.analyticalModelRadioButton.TabIndex = 9;
            this.analyticalModelRadioButton.TabStop = true;
            this.analyticalModelRadioButton.Text = "Analytical Model";
            this.analyticalModelRadioButton.UseVisualStyleBackColor = true;
            this.analyticalModelRadioButton.CheckedChanged += new System.EventHandler(this.analyticalModelRadioButton_CheckedChanged);
            //
            // previewBox
            //
            this.previewBox.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.previewBox.BorderStyle = System.Windows.Forms.BorderStyle.Fixed3D;
            this.previewBox.Cursor = System.Windows.Forms.Cursors.Cross;
            this.previewBox.Location = new System.Drawing.Point(12, 25);
            this.previewBox.Name = "previewBox";
            this.previewBox.Size = new System.Drawing.Size(472, 425);
            this.previewBox.TabIndex = 4;
            this.previewBox.TabStop = false;
            this.previewBox.Paint += new System.Windows.Forms.PaintEventHandler(this.previewBox_Paint);
            //
            // groupBox1
            //
            this.groupBox1.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
            this.groupBox1.Controls.Add(this.label5);
            this.groupBox1.Controls.Add(this.viewDirectionComboBox);
            this.groupBox1.Controls.Add(this.label4);
            this.groupBox1.Controls.Add(this.viewListBox);
            this.groupBox1.Controls.Add(this.label1);
            this.groupBox1.Controls.Add(this.detailLevelComboBox);
            this.groupBox1.Controls.Add(this.physicalModelRadioButton);
            this.groupBox1.Controls.Add(this.analyticalModelRadioButton);
            this.groupBox1.Location = new System.Drawing.Point(12, 456);
            this.groupBox1.Name = "groupBox1";
            this.groupBox1.Size = new System.Drawing.Size(472, 160);
            this.groupBox1.TabIndex = 6;
            this.groupBox1.TabStop = false;
            this.groupBox1.Text = "Preview";
            //
            // label5
            //
            this.label5.AutoSize = true;
            this.label5.Location = new System.Drawing.Point(6, 65);
            this.label5.Name = "label5";
            this.label5.Size = new System.Drawing.Size(78, 13);
            this.label5.TabIndex = 11;
            this.label5.Text = "View Direction:";
            //
            // viewDirectionComboBox
            //
            this.viewDirectionComboBox.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
            this.viewDirectionComboBox.FormattingEnabled = true;
            this.viewDirectionComboBox.Items.AddRange(new object[] {
            "Top",
            "Front",
            "Left",
            "Right",
            "Bottom",
            "Back",
            "IsoMetric"});
            this.viewDirectionComboBox.Location = new System.Drawing.Point(6, 82);
            this.viewDirectionComboBox.Name = "viewDirectionComboBox";
            this.viewDirectionComboBox.Size = new System.Drawing.Size(158, 21);
            this.viewDirectionComboBox.TabIndex = 6;
            this.viewDirectionComboBox.SelectedIndexChanged += new System.EventHandler(this.viewDirectionComboBox_SelectedIndexChanged);
            //
            // label4
            //
            this.label4.AutoSize = true;
            this.label4.Location = new System.Drawing.Point(167, 16);
            this.label4.Name = "label4";
            this.label4.Size = new System.Drawing.Size(82, 13);
            this.label4.TabIndex = 9;
            this.label4.Text = "Displayed View:";
            //
            // viewListBox
            //
            this.viewListBox.FormattingEnabled = true;
            this.viewListBox.Location = new System.Drawing.Point(170, 33);
            this.viewListBox.Name = "viewListBox";
            this.viewListBox.Size = new System.Drawing.Size(296, 121);
            this.viewListBox.TabIndex = 7;
            this.viewListBox.SelectedIndexChanged += new System.EventHandler(this.viewListBox_SelectedIndexChanged);
            //
            // label1
            //
            this.label1.AutoSize = true;
            this.label1.Location = new System.Drawing.Point(6, 16);
            this.label1.Name = "label1";
            this.label1.Size = new System.Drawing.Size(66, 13);
            this.label1.TabIndex = 7;
            this.label1.Text = "Detail Level:";
            //
            // detailLevelComboBox
            //
            this.detailLevelComboBox.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
            this.detailLevelComboBox.FormattingEnabled = true;
            this.detailLevelComboBox.Items.AddRange(new object[] {
            "Undefined",
            "Coarse",
            "Medium",
            "Fine"});
            this.detailLevelComboBox.Location = new System.Drawing.Point(6, 33);
            this.detailLevelComboBox.Name = "detailLevelComboBox";
            this.detailLevelComboBox.Size = new System.Drawing.Size(158, 21);
            this.detailLevelComboBox.TabIndex = 5;
            this.detailLevelComboBox.SelectedIndexChanged += new System.EventHandler(this.detailLevelComboBox_SelectedIndexChanged);
            //
            // label2
            //
            this.label2.AutoSize = true;
            this.label2.Location = new System.Drawing.Point(13, 8);
            this.label2.Name = "label2";
            this.label2.Size = new System.Drawing.Size(48, 13);
            this.label2.TabIndex = 7;
            this.label2.Text = "Preview:";
            //
            // label3
            //
            this.label3.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
            this.label3.AutoSize = true;
            this.label3.Location = new System.Drawing.Point(491, 8);
            this.label3.Name = "label3";
            this.label3.Size = new System.Drawing.Size(63, 13);
            this.label3.TabIndex = 8;
            this.label3.Text = "Parameters:";
            //
            // okButton
            //
            this.okButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
            this.okButton.Location = new System.Drawing.Point(738, 598);
            this.okButton.Name = "okButton";
            this.okButton.Size = new System.Drawing.Size(75, 23);
            this.okButton.TabIndex = 11;
            this.okButton.Text = "&OK";
            this.okButton.UseVisualStyleBackColor = true;
            this.okButton.Click += new System.EventHandler(this.OKButton_Click);
            //
            // closeButton
            //
            this.closeButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
            this.closeButton.DialogResult = System.Windows.Forms.DialogResult.Cancel;
            this.closeButton.Location = new System.Drawing.Point(819, 598);
            this.closeButton.Name = "closeButton";
            this.closeButton.Size = new System.Drawing.Size(75, 23);
            this.closeButton.TabIndex = 12;
            this.closeButton.Text = "&Cancel";
            this.closeButton.UseVisualStyleBackColor = true;
            this.closeButton.Click += new System.EventHandler(this.closeButton_Click);
            //
            // ObjectViewerForm
            //
            this.AcceptButton = this.okButton;
            this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
            this.CancelButton = this.closeButton;
            this.ClientSize = new System.Drawing.Size(906, 628);
            this.Controls.Add(this.closeButton);
            this.Controls.Add(this.okButton);
            this.Controls.Add(this.label3);
            this.Controls.Add(this.label2);
            this.Controls.Add(this.groupBox1);
            this.Controls.Add(this.previewBox);
            this.Controls.Add(this.parametersDataGridView);
            this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog;
            this.MaximizeBox = false;
            this.MinimizeBox = false;
            this.Name = "ObjectViewerForm";
            this.ShowInTaskbar = false;
            this.Text = "Object Viewer";
            this.FormClosed += new System.Windows.Forms.FormClosedEventHandler(this.ObjectViewerForm_FormClosed);
            this.Load += new System.EventHandler(this.ObjectViewerForm_Load);
            ((System.ComponentModel.ISupportInitialize)(this.parametersDataGridView)).EndInit();
            ((System.ComponentModel.ISupportInitialize)(this.previewBox)).EndInit();
            this.groupBox1.ResumeLayout(false);
            this.groupBox1.PerformLayout();
            this.ResumeLayout(false);
            this.PerformLayout();

        }

        #endregion

        private System.Windows.Forms.DataGridView parametersDataGridView;
        private System.Windows.Forms.RadioButton physicalModelRadioButton;
        private System.Windows.Forms.RadioButton analyticalModelRadioButton;
        private System.Windows.Forms.PictureBox previewBox;
        private System.Windows.Forms.GroupBox groupBox1;
        private System.Windows.Forms.ComboBox detailLevelComboBox;
        private System.Windows.Forms.Label label1;
        private System.Windows.Forms.ListBox viewListBox;
        private System.Windows.Forms.Label label2;
        private System.Windows.Forms.Label label3;
        private System.Windows.Forms.Button okButton;
        private System.Windows.Forms.Button closeButton;
        private System.Windows.Forms.Label label4;
        private System.Windows.Forms.Label label5;
        private System.Windows.Forms.ComboBox viewDirectionComboBox;
    }
}

Para.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.Data;
using System.Runtime.Serialization;

using Autodesk.Revit;
using Autodesk.Revit.DB;

namespace Revit.SDK.Samples.ObjectViewer.CS
{
    /// <summary>
    /// a class used to define a parameter of Element
    /// </summary>
    public class Para
    {
        private Parameter m_parameter;   //one parameter of a element

        /// <summary>
        /// parameter's name
        /// </summary>
        public string ParaName
        {
            get
            {
                try
                {
                    return m_parameter.Definition.Name;
                }
                catch
                {
                    return null;
                }
            }
        }

        /// <summary>
        /// parameter's value
        /// </summary>
        //public Object Value // jeremy
        public string Value // jeremy
        {
            get
            {
                try
                {
                    return GetParameterValue(m_parameter);
                }
                catch
                {
                    return null;
                }
            }
            set
            {
                try
                {
                    SetParameterValue(m_parameter,value);
                }
                catch
                {
                }
            }
        }

        /// <summary>
        /// get parameter's value
        /// </summary>
        /// <param name="parameter">parameter of Element</param>
        /// <returns>parameter's value include unit if have</returns>
        public static string GetParameterValue(Parameter parameter) // jeremy
        //public static Object GetParameterValue(Parameter parameter) // jeremy
        {
            switch (parameter.StorageType)
            {
                case StorageType.Double:
                    //get value with unit, AsDouble() can get value without unit
                    return parameter.AsValueString();
                case StorageType.ElementId:
                    return parameter.AsElementId().IntegerValue.ToString();
                case StorageType.Integer:
                    //get value with unit, AsInteger() can get value without unit
                    return parameter.AsValueString();
                case StorageType.None:
                    return parameter.AsValueString();
                case StorageType.String:
                    return parameter.AsString();
                default:
                    return "";
            }
        }

        /// <summary>
        /// set parameter's value
        /// </summary>
        /// <param name="parameter">parameter of a Material</param>
        /// <param name="value">
        /// value will be set to parameter
        /// </param>
        public static void SetParameterValue(Parameter parameter, Object value)
        {
            //first,check whether this parameter is read only
            if(parameter.IsReadOnly)
            {
                return;
            }

            switch (parameter.StorageType)
            {
                case StorageType.Double:
                    //set value with unit, Set() can set value without unit
                    parameter.SetValueString(value as string);
                    break;
                case StorageType.ElementId:
                    Autodesk.Revit.DB.ElementId elementId = (Autodesk.Revit.DB.ElementId)(value);
                    parameter.Set(elementId);
                    break;
                case StorageType.Integer:
                    //set value with unit, Set() can set value without unit
                    parameter.SetValueString(value as string);
                    break;
                case StorageType.None:
                    parameter.SetValueString(value as string);
                    break;
                case StorageType.String:
                    parameter.Set(value as string);
                    break;
                default:
                    break;
            }
        }

        /// <summary>
        /// constructor of Para
        /// </summary>
        /// <param name="parameter"></param>
        public Para(Parameter parameter)
        {
            m_parameter = parameter;
        }
    }
}

ParasFactory.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.ComponentModel;
using System.Windows.Forms;

using Autodesk.Revit;
using Autodesk.Revit.DB;
using Autodesk.Revit.UI;

namespace Revit.SDK.Samples.ObjectViewer.CS
{
    /// <summary>
    /// a class used to create BindingList
    /// </summary>
    public class ParasFactory
    {
        private Element m_element;  // element that selected in Revit by Mouse
        private SortableBindingList<Para> m_parasList;  //a list store parameters' information


        /// <summary>
        /// constructor of ParametersFactory
        /// </summary>
        /// <param name="element">the element of which parameters will be gotten</param>
        public ParasFactory(Element element)
        {
            m_element = element;
            m_parasList = new SortableBindingList<Para>();
            //set m_parasList can be edit;
            m_parasList.AllowEdit = true;
        }


        /// <summary>
        /// add Para's instances to m_parasList, finally return it
        /// </summary>
        /// <returns></returns>
        public SortableBindingList<Para> CreateParas()
        {
            try
            {
                ParameterSetIterator iterator = m_element.Parameters.ForwardIterator();
                Parameter parameter;
                iterator.Reset();

                //use Iterator to loop, new a Para for each parameter and
                //add it to m_parameter; if failed return null
                for (; iterator.MoveNext(); )
                {
                    parameter = iterator.Current as Parameter;
                    if (null != parameter)
                    {
                        m_parasList.Add(new Para(parameter));
                    }
                }
            }
            catch (Exception e)
            {
                string errorText = "Create Paras failed: " + e.Message;
                TaskDialog.Show("Revit", errorText);
                return null;
            }
            return m_parasList;
        }
    }
}

Sketch.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 System.Drawing.Drawing2D;
using System.Collections.ObjectModel;

using Autodesk.Revit.DB;

namespace Revit.SDK.Samples.ObjectViewer.CS
{
    /// <summary>
    /// provide methods to draw objects
    /// </summary>
    public class Sketch3D
    {
        // ratio of margin to canvas width
        private const float MarginRatio = 0.1f;
        // every frame distance to move the drawing
        private const float MoveDistance = 2.0f;
        // every frame ratio to zoom the drawing
        private const float ZoomRatio = 0.05f;
        // scale ratio of region showing the 2D geometry to the whole display region
        private const float Drawing2DScale2D = 0.5f;

        private List<PointF[]> m_curve3Ds = new List<PointF[]>();   // all 3D points
        private List<PointF[]> m_curve2Ds = new List<PointF[]>();   // all 2D points
        private Graphics3DData m_data3D;        // 3D graphic data
        private Graphics2DData m_data2D;        // 2D graphic data
        // defines a local geometric transform
        private Matrix m_transform3D;
        private Matrix m_transform2D;
        // displayed BoundingBox in canvas
        private RectangleF m_displayBBox = new RectangleF(0.0f, 0.0f, 10.0f, 10.0f);

        /// <summary>
        /// Displayed BoundingBox in canvas
        /// </summary>
        public RectangleF DisplayBBox
        {
            get
            {
                return m_displayBBox;
            }
            set
            {
                m_displayBBox = value;
                Calculate2DTransform();
                Calculate3DTransform();
                UpdateDataAndEvent();
                if (null != UpdateViewEvent)
                {
                    UpdateViewEvent();
                }
            }
        }

        /// <summary>
        /// 2D graphic data
        /// </summary>
        public Graphics2DData Data2D
        {
            get
            {
                return m_data2D;
            }
            set
            {
                m_data2D = value;
                Calculate2DTransform();
                UpdateDataAndEvent();
                if (null != UpdateViewEvent)
                {
                    UpdateViewEvent();
                }
            }
        }

        /// <summary>
        /// 3D graphic data
        /// </summary>
        public Graphics3DData Data3D
        {
            get
            {
                return m_data3D;
            }
            set
            {
                m_data3D = value;
                UpdateDataAndEvent();
                if (null != UpdateViewEvent)
                {
                    UpdateViewEvent();
                }
            }
        }

        /// <summary>
        /// view data update
        /// </summary>
        public event UpdateViewDelegate UpdateViewEvent;

        /// <summary>
        /// The default constructor
        /// </summary>
        /// <param name="data3D">a list contain all the 3d data</param>
        /// <param name="data2D">a list contain all the 3d data</param>
        public Sketch3D(Graphics3DData data3D, Graphics2DData data2D)
        {
            m_data3D = data3D;
            m_data2D = data2D;
            Calculate3DTransform();
            Calculate2DTransform();
            UpdateDataAndEvent();
        }

        /// <summary>
        /// draw the line contain in m_lines in 2d Preview
        /// </summary>
        /// <param name="graphics">Graphics to draw</param>
        /// <returns></returns>
        public void Draw(Graphics graphics)
        {
            if (m_data3D.NumPoints == 0 && m_data2D.NumPoints == 0)
            {
                graphics.Clear(System.Drawing.Color.LightGray);
                return;
            }

            graphics.Clear(System.Drawing.Color.White);
            DrawCurves(graphics, m_curve3Ds, m_transform3D, new Pen(System.Drawing.Color.DarkGreen));
            DrawCurves(graphics, m_curve2Ds, m_transform2D, new Pen(System.Drawing.Color.DarkBlue));
            if (m_data2D.NumPoints > 0)
            {
                GraphicsPath gPath = new GraphicsPath();
                gPath.AddRectangle(m_data2D.BBox);
                gPath.Transform(m_transform2D);
                SolidBrush brush = new SolidBrush(System.Drawing.Color.FromArgb(70, System.Drawing.Color.LightSkyBlue));
                graphics.FillPath(brush, gPath);
            }
        }

        /// <summary>
        /// zoom the displayed drawing in canvas
        /// </summary>
        /// <param name="zoomIn">true for zoom in, false for zoom out</param>
        public void Zoom(bool zoomIn)
        {
            if (zoomIn)
            {
                m_transform3D.Scale(1f - ZoomRatio, 1f - ZoomRatio, MatrixOrder.Append);
            }
            else
            {
                m_transform3D.Scale(1f + ZoomRatio, 1f + ZoomRatio, MatrixOrder.Append);
            }
            if (null != UpdateViewEvent)
            {
                UpdateViewEvent();
            }
        }

        /// <summary>
        /// move view in horizontal direction
        /// </summary>
        /// <param name="left">left or right</param>
        public void MoveX(bool left)
        {
            if (left)
            {
                m_transform3D.Translate(-MoveDistance, 0, MatrixOrder.Append);
            }
            else
            {
                m_transform3D.Translate(MoveDistance, 0, MatrixOrder.Append);
            }
            if (null != UpdateViewEvent)
            {
                UpdateViewEvent();
            }
        }

        /// <summary>
        /// move view in vertical direction
        /// </summary>
        /// <param name="up">up or down</param>
        public void MoveY(bool up)
        {
            if (up)
            {
                m_transform3D.Translate(0, MoveDistance, MatrixOrder.Append);
            }
            else
            {
                m_transform3D.Translate(0, -MoveDistance, MatrixOrder.Append);
            }
            if (null != UpdateViewEvent)
            {
                UpdateViewEvent();
            }
        }

        /// <summary>
        /// update drawing data according and related Event
        /// </summary>
        private void UpdateDataAndEvent()
        {
            Initialize3DData();
            Initialize2DData();
            m_data3D.UpdateViewEvent += new UpdateViewDelegate(DataUpdateViewEvent);
        }

        /// <summary>
        /// initialize 3D drawing data
        /// </summary>
        private void Initialize3DData()
        {
            m_curve3Ds.Clear();
            foreach (List<Vector> vectors in m_data3D.ConstCurves)
            {
                PointF[] pnts = new PointF[vectors.Count];
                m_curve3Ds.Add(pnts);
                for (int i = 0; i < vectors.Count; i++)
                {
                    pnts[i] = new PointF((float)vectors[i].X, (float)vectors[i].Y);
                }
            }
        }

        /// <summary>
        /// initialize 2D drawing data
        /// </summary>
        private void Initialize2DData()
        {
            m_curve2Ds.Clear();
            m_curve2Ds.AddRange(m_data2D.ConstCurves);
        }

        /// <summary>
        /// Draw curves
        /// </summary>
        /// <param name="graphics">graphics handle</param>
        /// <param name="curves">curves to be drawn</param>
        /// <param name="transform">transform between canvas and graphic data</param>
        /// <param name="pen">pen to draw</param>
        private void DrawCurves(Graphics graphics, List<PointF[]> curves, Matrix transform, Pen pen)
        {
            foreach (PointF[] curve in curves)
            {
                GraphicsPath gPath = new GraphicsPath();
                if (curve.Length == 0)
                {
                    break;
                }
                if (curve.Length == 1)
                {
                    gPath.AddArc(new RectangleF(curve[0], new SizeF(0.5f, 0.5f)), 0.0f, (float)Math.PI);
                }
                else
                {
                    gPath.AddLines(curve);
                }
                gPath.Transform(transform);
                graphics.DrawPath(pen, gPath);
            }
        }

        /// <summary>
        /// update according data when 3D data is updated
        /// </summary>
        private void DataUpdateViewEvent()
        {
            Initialize3DData();
            if (null != UpdateViewEvent)
            {
                UpdateViewEvent();
            }
        }

        /// <summary>
        /// calculate the transform between canvas and 3D geometry objects
        /// </summary>
        private void Calculate3DTransform()
        {
            float previewWidth = m_displayBBox.Width;
            float previewHeight = m_displayBBox.Height;
            RectangleF bbox3D = ToBoundingBox2D(m_data3D.BBox);
            PointF[] plgpts3D = CalculateCanvasRegion(previewWidth, previewHeight, bbox3D);
            m_transform3D = new Matrix(PreTreatBBox(bbox3D), plgpts3D);
        }

        /// <summary>
        /// calculate the transform between canvas and 2D geometry objects
        /// </summary>
        private void Calculate2DTransform()
        {
            float previewWidth = m_displayBBox.Width;
            float previewHeight = m_displayBBox.Height;
            float previewWidth2D = previewWidth * Drawing2DScale2D;
            float previewHeight2D = previewHeight * Drawing2DScale2D;
            PointF[] plgpts2D = CalculateCanvasRegion(previewWidth2D, previewHeight2D, m_data2D.BBox);
            m_transform2D = new Matrix(PreTreatBBox(m_data2D.BBox), plgpts2D);
        }

        /// <summary>
        /// pretreat BBoundingBox so that its height or width will be bigger than zero
        /// </summary>
        /// <param name="bbox"></param>
        /// <returns></returns>
        private RectangleF PreTreatBBox(RectangleF bbox)
        {
            const float EpsilonLen = 0.001f;
            if (bbox.Height < EpsilonLen)
            {
                bbox.Height = 0.001f;
            }
            if (bbox.Width < EpsilonLen)
            {
                bbox.Width = 0.001f;
            }
            return bbox;
        }

        /// <summary>
        /// get the display region, adjust the proportion and location
        /// </summary>
        /// <returns>upper-left, upper-right, and lower-left corners of the rectangle </returns>
        private PointF[] CalculateCanvasRegion(float previewWidth, float previewHeigh, RectangleF bbox)
        {
            // get the area without margin
            float realWidth = previewWidth * (1 - 2 * MarginRatio);
            float realHeight = previewHeigh * (1 - 2 * MarginRatio);
            float minX = previewWidth * MarginRatio;
            float minY = previewHeigh * MarginRatio;
            // ratio of width to height
            float originRate = bbox.Width / bbox.Height;
            float displayRate = realWidth / realHeight;

            if (originRate > displayRate)
            {
                // display area in canvas need move to center in height
                float goalHeight = realWidth / originRate;
                minY = minY + (realHeight - goalHeight) / 2;
                realHeight = goalHeight;
            }
            else
            {
                // display area in canvas need move to center in width
                float goalWidth = realHeight * originRate;
                minX = minX + (realWidth - goalWidth) / 2;
                realWidth = goalWidth;
            }

            PointF[] plgpts = new PointF[3];
            plgpts[0] = new PointF(minX, realHeight + minY);                // upper-left point
            plgpts[1] = new PointF(realWidth + minX, realHeight + minY);    // upper-right point
            plgpts[2] = new PointF(minX, minY);                             // lower-left point

            return plgpts;
        }

        /// <summary>
        /// Get X and Y data of 3D BoundingBox to creat a 2D BoundingBox
        /// </summary>
        /// <param name="bbox3D"></param>
        /// <returns></returns>
        private RectangleF ToBoundingBox2D(BoundingBoxXYZ bbox3D)
        {
            float left = (float)(bbox3D.Min.X);
            float up = (float)(bbox3D.Min.Y);
            float right = (float)(bbox3D.Max.X);
            float down = (float)(bbox3D.Max.Y);
            RectangleF bbox2D = new RectangleF(left, up, (right - left), (down - up));
            return bbox2D;
        }
    }
}

UCS.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;

namespace Revit.SDK.Samples.ObjectViewer.CS
{
    /// <summary>
    /// This class stand for user coordinate system
    /// </summary>
    public class UCS
    {
        Vector m_origin = new Vector(0.0, 0.0, 0.0);
        Vector m_xAxis = new Vector(1.0, 0.0, 0.0);
        Vector m_yAxis = new Vector(0.0, 1.0, 0.0);
        Vector m_zAxis = new Vector(0.0, 0.0, 1.0);

        /// <summary>
        /// constructor
        /// </summary>
        public UCS()
        {
        }

        /// <summary>
        /// Property to get origin of user coordinate system
        /// </summary>
        public Vector Origin
        {
            get
            {
                return m_origin;
            }
            set
            {
                m_origin = value;
            }
        }

        /// <summary>
        /// Property to get Axis(x,y,z) of User coordinate system
        /// </summary>
        /// <param name="index">indicate which Axis want be gotten</param>
        /// <returns>information about the Axis</returns>
        public Vector this[int index]
        {
            get
            {
                switch (index)
                {
                    case 0:
                        return m_xAxis;
                    case 1:
                        return m_yAxis;
                    case 2:
                        return m_zAxis;
                    default:
                        throw new ArgumentOutOfRangeException();
                }
            }
        }

        /// <summary>
        /// Property to get X Axis of user coordinate system
        /// </summary>
        public Vector XAxis
        {
            get
            {
                return m_xAxis;
            }
        }

        /// <summary>
        /// Property to get Y Axis of user coordinate system
        /// </summary>
        public Vector YAxis
        {
            get
            {
                return m_yAxis;
            }
        }

        /// <summary>
        /// Property to get Z Axis of user coordinate system
        /// </summary>
        public Vector ZAxis
        {
            get
            {
                return m_zAxis;
            }
        }

        /// <summary>
        /// The default constructor,
        /// </summary>
        public UCS(Vector origin, Vector xAxis, Vector yAxis)
            : this(origin, xAxis, yAxis, true)
        {
        }

        /// <summary>
        /// constructor,
        /// get a user coordinate system
        /// </summary>
        /// <param name="origin">origin of user coordinate system</param>
        /// <param name="xAxis">xAxis of user coordinate system</param>
        /// <param name="yAxis">yAxis of user coordinate system</param>
        /// <param name="flag"></param>
        public UCS(Vector origin, Vector xAxis, Vector yAxis, bool flag)
        {
            Vector x2 = xAxis / ~xAxis;
            Vector y2 = yAxis / ~yAxis;
            Vector z2 = x2 & y2;
            if (~z2 < double.Epsilon)
            {
                throw new InvalidOperationException();
            }

            if (!flag)
            {
                z2 = -z2;
            }

            m_origin = origin;
            m_xAxis = x2;
            m_yAxis = y2;
            m_zAxis = z2;
        }

        /// <summary>
        /// Transform local coordinate to global coordinate
        /// </summary>
        /// <param name="arg">a vector which need to transform</param>
        public Vector LC2GC(Vector arg)
        {
            Vector result = new Vector();
            result.X =
                arg.X * m_xAxis.X + arg.Y * m_yAxis.X + arg.Z * m_zAxis.X + m_origin.X;
            result.Y =
                arg.X * m_xAxis.Y + arg.Y * m_yAxis.Y + arg.Z * m_zAxis.Y + m_origin.Y;
            result.Z =
                arg.X * m_xAxis.Z + arg.Y * m_yAxis.Z + arg.Z * m_zAxis.Z + m_origin.Z;
            return result;
        }

        /// <summary>
        /// Transform global coordinate to local coordinate
        /// </summary>
        /// <param name="arg">a vector which need to transform</param>
        public Vector GC2LC(Vector arg)
        {
            Vector result = new Vector();
            arg = arg - m_origin;
            result.X = m_xAxis * arg;
            result.Y = m_yAxis * arg;
            result.Z = m_zAxis * arg;
            return result;
        }

        /// <summary>
        /// add 2 UCS
        /// </summary>
        /// <param name="lhs"></param>
        /// <param name="rhs"></param>
        /// <returns></returns>
        public static UCS operator +(UCS lhs, UCS rhs)
        {
            Vector origin = lhs.Origin + rhs.Origin;

            Vector[] left = new Vector[3];
            Vector[] right = new Vector[3];

            for (int i = 0; i < 3; i++)
            {
                left[i] = lhs[i];
                right[i] = rhs[i];
            }

            Vector[] basis = Vector.MultiCross3X3Matrix(left, right);

            return new UCS(origin, basis[0], basis[1]);
        }
    }
}

Vector.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;

namespace Revit.SDK.Samples.ObjectViewer.CS
{
    /// <summary>
    /// Point class use to store point coordinate value
    /// and get the value via (x, y ,z)property
    /// </summary>
    public struct Vector
    {
        //private member
        private double m_x; //x coordinate of vector
        private double m_y; //y coordinate of vector
        private double m_z; //z coordinate of vector

        /// <summary>
        /// Property to get X coordinate
        /// </summary>
        public double X
        {
            get
            {
                return m_x;
            }
            set
            {
                m_x = value;
            }
        }

        /// <summary>
        /// Property to get Y coordinate
        /// </summary>
        public double Y
        {
            get
            {
                return m_y;
            }
            set
            {
                m_y = value;
            }
        }

        /// <summary>
        /// Property to get Z coordinate
        /// </summary>
        public double Z
        {
            get
            {
                return m_z;
            }
            set
            {
                m_z = value;
            }
        }

        /// <summary>
        /// Property to get x, y, z coordinate bu index 1, 2, 3
        /// </summary>
        public double this[int index]
        {
            get
            {
                switch (index)
                {
                    case 0:
                        return m_x;
                    case 1:
                        return m_y;
                    case 2:
                        return m_z;
                    default:
                        throw new ArgumentOutOfRangeException();
                }
            }
            set
            {
                switch (index)
                {
                    case 0:
                        m_x = value;
                        break;
                    case 1:
                        m_y = value;
                        break;
                    case 2:
                        m_z = value;
                        break;
                    default:
                        throw new ArgumentOutOfRangeException();
                }
            }
        }

        /// <summary>
        /// copy constructor
        /// </summary>
        public Vector(Vector rhs)
        {
            m_x = rhs.X;
            m_y = rhs.Y;
            m_z = rhs.Z;
        }

        /// <summary>
        /// constructor
        /// </summary>
        /// <param name="x">x coordinate of point</param>
        /// <param name="y">y coordinate of point</param>
        /// <param name="z">z coordinate of point</param>
        public Vector(double x, double y, double z)
        {
            m_x = x;
            m_y = y;
            m_z = z;
        }

        /// <summary>
        /// get Normal by vector
        /// </summary>
        public Vector GetNormal()
        {
            Vector direct = new Vector();
            double len = GetLength();
            if (len < double.Epsilon)
            {
                return new Vector();
            }
            direct.X = m_x / len;
            direct.Y = m_y / len;
            direct.Z = m_z / len;
            return direct;
        }

        /// <summary>
        /// add two vector
        /// </summary>
        /// <param name="lhs">first vector</param>
        /// <param name="rhs">second vector</param>
        /// <returns>add two vector</returns>
        public static Vector operator +(Vector lhs, Vector rhs)
        {
            Vector result = new Vector(lhs);
            result.X += rhs.X;
            result.Y += rhs.Y;
            result.Z += rhs.Z;
            return result;
        }

        /// <summary>
        /// subtraction of two vector
        /// </summary>
        /// <param name="lhs">first vector</param>
        /// <param name="rhs">second vector</param>
        /// <returns>subtraction of two vector</returns>
        public static Vector operator -(Vector lhs, Vector rhs)
        {
            Vector result = new Vector(lhs);
            result.X -= rhs.X;
            result.Y -= rhs.Y;
            result.Z -= rhs.Z;
            return result;
        }

        /// <summary>
        /// negative of vector
        /// </summary>
        /// <param name="lhs">vector</param>
        /// <returns>negative of vector</returns>
        public static Vector operator -(Vector lhs)
        {
            Vector result = new Vector(lhs);
            result.X = -lhs.X;
            result.Y = -lhs.Y;
            result.Z = -lhs.Z;
            return result;
        }

        /// <summary>
        /// get normal vector of two vector
        /// </summary>
        /// <param name="lhs">first vector</param>
        /// <param name="rhs">second vector</param>
        /// <returns> normal vector of two vector</returns>
        public static Vector operator &(Vector lhs, Vector rhs)
        {
            double v1 = lhs.X;
            double v2 = lhs.Y;
            double v3 = lhs.Z;

            double u1 = rhs.X;
            double u2 = rhs.Y;
            double u3 = rhs.Z;

            double x = v2 * u3 - v3 * u2;
            double y = v3 * u1 - v1 * u3;
            double z = v1 * u2 - v2 * u1;

            return new Vector(x, y, z);
        }

        /// <summary>
        /// get cross vector of two vector
        /// </summary>
        /// <param name="lhs">first vector</param>
        /// <param name="rhs">second vector</param>
        /// <returns> cross vector of two vector</returns>
        public static double operator *(Vector lhs, Vector rhs)
        {
            return lhs.X * rhs.X + lhs.Y * rhs.Y + lhs.Z * rhs.Z;
        }

        /// <summary>
        /// get vector multiply by an double value
        /// </summary>
        /// <param name="lhs">vector</param>
        /// <param name="rhs">double value</param>
        /// <returns> vector multiply by an double value</returns>
        public static Vector operator *(Vector lhs, double rhs)
        {
            return new Vector(lhs.X * rhs, lhs.Y * rhs, lhs.Z * rhs);
        }

        /// <summary>
        /// estimate whether two are unequal
        /// </summary>
        /// <param name="lhs">first vector</param>
        /// <param name="rhs">second vector</param>
        /// <returns> whether two are unequal</returns>
        public static bool operator !=(Vector lhs, Vector rhs)
        {
            return !IsEqual(lhs, rhs);
        }

        /// <summary>
        /// estimate whether two are equal
        /// </summary>
        /// <param name="lhs">first vector</param>
        /// <param name="rhs">second vector</param>
        /// <returns> whether two are equal</returns>
        public static bool operator ==(Vector lhs, Vector rhs)
        {
            return IsEqual(lhs, rhs);
        }

        /// <summary>
        /// get the length of vector
        /// </summary>
        /// <param name="lhs">vector</param>
        /// <returns>length of vector</returns>
        public static double operator ~(Vector lhs)
        {
            return lhs.GetLength();
        }

        /// <summary>
        /// get vector divided by an double value
        /// </summary>
        /// <param name="lhs">vector</param>
        /// <param name="rhs">double value</param>
        /// <returns> vector divided by an double value</returns>
        public static Vector operator /(Vector lhs, double rhs)
        {
            return new Vector(lhs.m_x / rhs, lhs.m_y / rhs, lhs.m_z / rhs);
        }

        /// <summary>
        /// get angle of two vector
        /// </summary>
        /// <param name="lhs">first vector</param>
        /// <param name="rhs">second vector</param>
        /// <param name="acuteAngleDesired">
        /// indicate whether get the acute angle of two angles between two vectors
        /// </param>
        /// <returns> angle of two vector</returns>
        public static double GetAngleOf2Vectors(Vector lhs, Vector rhs, bool acuteAngleDesired)
        {
            double angle = Math.Acos(lhs.GetNormal() * rhs.GetNormal());
            if (acuteAngleDesired && angle > Math.PI / 2)
            {
                angle = Math.PI - angle;
            }
            return angle;
        }

        /// <summary>
        /// estimate whether two are equal
        /// </summary>
        /// <param name="obj">object which compare with</param>
        /// <returns> whether two are equal</returns>
        public override bool Equals(object obj)
        {
            try
            {
                Vector rhs = (Vector)obj;
                return IsEqual(this, rhs);
            }
            catch
            {
            }
            return false;
        }

        /// <summary>
        /// Get HashCode
        /// </summary>
        public override int GetHashCode()
        {
            return m_x.GetHashCode() ^ m_y.GetHashCode() ^ m_z.GetHashCode();
        }

        /// <summary>
        /// Get Length of vector
        /// </summary>
        public double GetLength()
        {
            return Math.Sqrt(m_x*m_x + m_y*m_y + m_z*m_z);
        }

        /// <summary>
        /// estimate whether two vector are equal
        /// </summary>
        /// <param name="lhs">first vector</param>
        /// <param name="rhs">second vector</param>
        /// <returns> whether two are equal</returns>
        private static bool IsEqual(Vector lhs, Vector rhs)
        {
            if (lhs.X == rhs.X && lhs.X == rhs.X && lhs.X == rhs.X)
            {
                return true;
            }
            else
            {
                return false;
            }
        }

        /// <summary>
        /// Cross multiply 2 3*3 Matrix
        /// </summary>
        /// <param name="m1"></param>
        /// <param name="m2"></param>
        /// <returns>result 3*3 Matrix</returns>
        public static Vector[] MultiCross3X3Matrix(Vector[] m1, Vector[] m2)
        {
            Vector[] result = new Vector[3];

            for (int i = 0; i < 3; i++)
            {
                for (int j = 0; j < 3; j++)
                {
                    for (int k = 0; k < 3; k++)
                    {
                        switch (j)
                        {
                            case 0:
                                (result[i]).X += (m1[i])[k] * (m2[k])[j];
                                break;
                            case 1:
                                (result[i]).Y += (m1[i])[k] * (m2[k])[j];
                                break;
                            case 2:
                                (result[i]).Z += (m1[i])[k] * (m2[k])[j];
                                break;
                            default:
                                break;
                        }
                    }
                }
            }

            return result;
        }
    }
}