应用程序:NewPathReinforcement

Revit平台:结构

Revit版本:2011.0

首次发布版本:2008.0

编程语言:C#

技能水平:中等

类别:结构、几何

类型:ExternalCommand

主题:创建PathReinforcement

摘要:

本示例演示了如何使用API创建PathReinforcement

类:

Autodesk.Revit.UI.IExternalCommand

Autodesk.Revit.DB.Structure.PathReinforcement

Autodesk.Revit.DB.Structure.PathReinforcementType

Autodesk.Revit.DB.Face

Autodesk.Revit.DB.Edge

Autodesk.Revit.DB.Solid

Autodesk.Revit.Creation.Document

项目文件:

Command.cs

该文件包含了实现IExternalCommand接口的命令类Command。它获取所选的墙壁或地板,并在其上创建PathReinforcement

 

NewPathReinforcementForm.cs

该文件包含了一个继承自FormNewPathReinforcementForm类,其功能是显示所选的墙壁或地板的剖面,并允许用户在其上绘制PathReinforcement的路径。

 

Profile.cs

该文件包含了一个由ProfileWallProfileFloor派生而来的抽象类ProfileProfile包括两种方法Draw2D()CreatePathReinforcement()Draw2D()用于在表单上绘制轮廓,CreatePathReinforcement()用于在所选的墙壁或地板上创建PathReinforcement

 

LineTool.cs

该文件包含了一个名为LineTool的类,该类提供一些在表单上绘制线条并存储所绘线条数据的方法。

 

MathTools.cs

该文件包含了Verctor4Matrix4两个类。这两个类用于在3D2D之间转换点。

描述:

本示例展示了如何通过NewPathReinforcement(PathReinforcementType, Element, CurveArray, bool)方法创建PathReinforcement

- 使用Document.Create.NewPathReinforcementType()方法来在当前文档中创建一个PathReinforcementType

- 这里的Element代表PathReinforcement的宿主,即墙壁或地板。

- CurveArray存储PathReinforcement的路径。

- bool值指示PathReinforcement在路径的哪一侧。

操作说明:

1. 绘制结构墙壁或楼板(或打开NewPathReinforcement.rvt文件)并选择它。

2. 运行此命令。

3. 绘制您要创建的PathReinforcement路径,用户可以单击右键完成绘制。

4. 单击“预览”将预览将要创建的PathReinforcement

5. 选择或取消选择“翻转”复选框,以在您绘制的路径左侧或右侧创建PathReinforcement

6. 单击“创建”按钮,将在完成路径时创建PathReinforcement

7. 用户可以单击“清除”按钮清除并重新绘制路径的草图。

源代码

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

NewPathReinforcementForm.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.NewPathReinforcement.CS
{
    partial class NewPathReinforcementForm
    {
        /// <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.pictureBox = new System.Windows.Forms.PictureBox();
            this.createButton = new System.Windows.Forms.Button();
            this.cancelButton = new System.Windows.Forms.Button();
            this.flipCheckBox = new System.Windows.Forms.CheckBox();
            this.labelNote = new System.Windows.Forms.Label();
            this.previewButton = new System.Windows.Forms.Button();
            this.cleanButton = new System.Windows.Forms.Button();
            ((System.ComponentModel.ISupportInitialize)(this.pictureBox)).BeginInit();
            this.SuspendLayout();
            // 
            // pictureBox
            // 
            this.pictureBox.BackColor = System.Drawing.SystemColors.Window;
            this.pictureBox.BorderStyle = System.Windows.Forms.BorderStyle.Fixed3D;
            this.pictureBox.Location = new System.Drawing.Point(14, 12);
            this.pictureBox.Name = "pictureBox";
            this.pictureBox.Size = new System.Drawing.Size(446, 314);
            this.pictureBox.TabIndex = 0;
            this.pictureBox.TabStop = false;
            this.pictureBox.MouseDown += new System.Windows.Forms.MouseEventHandler(this.PictureBox_MouseDown);
            this.pictureBox.MouseMove += new System.Windows.Forms.MouseEventHandler(this.PictureBox_MouseMove);
            this.pictureBox.Paint += new System.Windows.Forms.PaintEventHandler(this.PictureBox_Paint);
            // 
            // createButton
            // 
            this.createButton.Font = new System.Drawing.Font("Verdana", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
            this.createButton.Location = new System.Drawing.Point(279, 403);
            this.createButton.Name = "createButton";
            this.createButton.Size = new System.Drawing.Size(87, 23);
            this.createButton.TabIndex = 1;
            this.createButton.Text = "C&reate";
            this.createButton.UseVisualStyleBackColor = true;
            this.createButton.Click += new System.EventHandler(this.CreateButton_Click);
            // 
            // cancelButton
            // 
            this.cancelButton.DialogResult = System.Windows.Forms.DialogResult.Cancel;
            this.cancelButton.Font = new System.Drawing.Font("Verdana", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
            this.cancelButton.Location = new System.Drawing.Point(373, 403);
            this.cancelButton.Name = "cancelButton";
            this.cancelButton.Size = new System.Drawing.Size(87, 23);
            this.cancelButton.TabIndex = 2;
            this.cancelButton.Text = "&Cancel";
            this.cancelButton.UseVisualStyleBackColor = true;
            this.cancelButton.Click += new System.EventHandler(this.CancelButton_Click);
            // 
            // flipCheckBox
            // 
            this.flipCheckBox.AutoSize = true;
            this.flipCheckBox.Font = new System.Drawing.Font("Verdana", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
            this.flipCheckBox.Location = new System.Drawing.Point(415, 332);
            this.flipCheckBox.Name = "flipCheckBox";
            this.flipCheckBox.Size = new System.Drawing.Size(45, 17);
            this.flipCheckBox.TabIndex = 3;
            this.flipCheckBox.Text = "Flip";
            this.flipCheckBox.UseVisualStyleBackColor = true;
            this.flipCheckBox.CheckedChanged += new System.EventHandler(this.FlipCheckBox_CheckedChanged);
            // 
            // labelNote
            // 
            this.labelNote.Font = new System.Drawing.Font("Verdana", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
            this.labelNote.Location = new System.Drawing.Point(11, 336);
            this.labelNote.Name = "labelNote";
            this.labelNote.Size = new System.Drawing.Size(245, 33);
            this.labelNote.TabIndex = 4;
            this.labelNote.Text = "Click mouse left button and drag to draw a curve. Right click to finish drawing.";
            // 
            // previewButton
            // 
            this.previewButton.Enabled = false;
            this.previewButton.Location = new System.Drawing.Point(373, 364);
            this.previewButton.Name = "previewButton";
            this.previewButton.Size = new System.Drawing.Size(87, 23);
            this.previewButton.TabIndex = 5;
            this.previewButton.Text = "&Preview";
            this.previewButton.UseVisualStyleBackColor = true;
            this.previewButton.Click += new System.EventHandler(this.ButtonPreview_Click);
            // 
            // cleanButton
            // 
            this.cleanButton.Location = new System.Drawing.Point(279, 364);
            this.cleanButton.Name = "cleanButton";
            this.cleanButton.Size = new System.Drawing.Size(87, 23);
            this.cleanButton.TabIndex = 6;
            this.cleanButton.Text = "C&lean";
            this.cleanButton.UseVisualStyleBackColor = true;
            this.cleanButton.Click += new System.EventHandler(this.ButtonClean_Click);
            // 
            // NewPathReinforcementForm
            // 
            this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 13F);
            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
            this.ClientSize = new System.Drawing.Size(472, 438);
            this.Controls.Add(this.cleanButton);
            this.Controls.Add(this.previewButton);
            this.Controls.Add(this.labelNote);
            this.Controls.Add(this.flipCheckBox);
            this.Controls.Add(this.cancelButton);
            this.Controls.Add(this.createButton);
            this.Controls.Add(this.pictureBox);
            this.Font = new System.Drawing.Font("Verdana", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
            this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog;
            this.MaximizeBox = false;
            this.MinimizeBox = false;
            this.Name = "NewPathReinforcementForm";
            this.ShowIcon = false;
            this.ShowInTaskbar = false;
            this.Text = "New Path Reinforcement";
            this.KeyPress += new System.Windows.Forms.KeyPressEventHandler(this.NewPathReinforcementForm_KeyPress);
            ((System.ComponentModel.ISupportInitialize)(this.pictureBox)).EndInit();
            this.ResumeLayout(false);
            this.PerformLayout();

        }

        #endregion

        private System.Windows.Forms.PictureBox pictureBox;
        private System.Windows.Forms.Button createButton;
        private System.Windows.Forms.Button cancelButton;
        private System.Windows.Forms.CheckBox flipCheckBox;
        private System.Windows.Forms.Label labelNote;
        private System.Windows.Forms.Button previewButton;
        private System.Windows.Forms.Button cleanButton;
    }
}

Profile.cs

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

namespace Revit.SDK.Samples.NewPathReinforcement.CS
{
   /// <summary>
   /// base class of ProfileFloor and ProfileWall
   /// contains the profile information and can make matrix to transform point to 2D plane
   /// </summary>
   public abstract class Profile
   {
      #region class member variables
      /// <summary>
      /// store all the points on the needed face
      /// </summary>
      protected List<List<XYZ>> m_points;

      /// <summary>
      /// object which contains reference to Revit Application
      /// </summary>
      protected Autodesk.Revit.UI.ExternalCommandData m_commandData;

      /// <summary>
      /// used to create new instances of utility objects. 
      /// </summary>
      protected Autodesk.Revit.Creation.Application m_appCreator;

      /// <summary>
      /// Revit DB document
      /// </summary>
      protected Autodesk.Revit.DB.Document m_document;

      /// <summary>
      /// store the Matrix used to transform 3D points to 2D
      /// </summary>
      protected Matrix4 m_to2DMatrix = null;
      #endregion

      /// <summary>
      /// CommandData property get object which contains reference to Revit Application
      /// </summary>
      public Autodesk.Revit.UI.ExternalCommandData CommandData
      {
         get
         {
            return m_commandData;
         }
      }

      /// <summary>
      /// To2DMatrix property to get Matrix used to transform 3D points to 2D
      /// </summary>
      public Matrix4 To2DMatrix
      {
         get
         {
            return m_to2DMatrix;
         }
      }

      /// <summary>
      /// constructor
      /// </summary>
      /// <param name="commandData">object which contains reference to Revit Application</param>
      protected Profile(ExternalCommandData commandData)
      {
         m_commandData = commandData;
         m_appCreator = m_commandData.Application.Application.Create;
         m_document = m_commandData.Application.ActiveUIDocument.Document;
      }

      /// <summary>
      /// abstract method to create PathReinforcement
      /// </summary>
      /// <returns>new created PathReinforcement</returns>
      /// <param name="points">points used to create PathReinforcement</param>
      /// <param name="flip">used to specify whether new PathReinforcement is Filp</param>
      public abstract PathReinforcement CreatePathReinforcement(List<Vector4> points, bool flip);

      /// <summary>
      /// Get points in first face
      /// </summary>
      /// <param name="faces">edges in all faces</param>
      /// <returns>points in first face</returns>
      public abstract List<List<XYZ>> GetNeedPoints(List<List<Edge>> faces);

      /// <summary>
      /// Get a matrix which can transform points to 2D
      /// </summary>
      public abstract Matrix4 GetTo2DMatrix();

      /// <summary>
      /// draw profile of wall or floor in 2D
      /// </summary>
      /// <param name="graphics">form graphic</param>
      /// <param name="pen">pen used to draw line in pictureBox</param>
      /// <param name="matrix4">Matrix used to transform 3d to 2d 
      /// and make picture in right scale </param>
      public void Draw2D(Graphics graphics, Pen pen, Matrix4 matrix4)
      {
         for (int i = 0; i < m_points.Count; i++)
         {
            List<XYZ> points = m_points[i];
            for (int j = 0; j < points.Count - 1; j++)
            {
               Autodesk.Revit.DB.XYZ point1 = points[j];
               Autodesk.Revit.DB.XYZ point2 = points[j + 1];

               Vector4 v1 = new Vector4(point1);
               Vector4 v2 = new Vector4(point2);

               v1 = matrix4.Transform(v1);
               v2 = matrix4.Transform(v2);
               graphics.DrawLine(pen, new Point((int)v1.X, (int)v1.Y),
                   new Point((int)v2.X, (int)v2.Y));
            }
         }
      }

      /// <summary>
      /// Get edges of element's profile
      /// </summary>
      /// <param name="elem">selected element</param>
      /// <returns>all the faces in the selected Element</returns>
      public List<List<Edge>> GetFaces(Autodesk.Revit.DB.Element elem)
      {
         List<List<Edge>> faceEdges = new List<List<Edge>>();
         Options options = m_appCreator.NewGeometryOptions();
         options.DetailLevel = ViewDetailLevel.Medium;
         options.ComputeReferences = true; //make sure references to geometric objects are computed.
         Autodesk.Revit.DB.GeometryElement geoElem = elem.get_Geometry(options);
         //GeometryObjectArray gObjects = geoElem.Objects;
         IEnumerator<GeometryObject> Objects = geoElem.GetEnumerator();
         //get all the edges in the Geometry object
         //foreach (GeometryObject geo in gObjects)
         while (Objects.MoveNext())
         {
            GeometryObject geo = Objects.Current;

            Solid solid = geo as Solid;
            if (solid != null)
            {
               FaceArray faces = solid.Faces;
               foreach (Face face in faces)
               {
                  EdgeArrayArray edgeArrarr = face.EdgeLoops;
                  foreach (EdgeArray edgeArr in edgeArrarr)
                  {
                     List<Edge> edgesList = new List<Edge>();
                     foreach (Edge edge in edgeArr)
                     {
                        edgesList.Add(edge);
                     }
                     faceEdges.Add(edgesList);
                  }
               }
            }
         }
         return faceEdges;
      }

      /// <summary>
      /// Get normal of face
      /// </summary>
      /// <param name="face">edges in a face</param>
      /// <returns>vector stands for normal of the face</returns>
      public Vector4 GetFaceNormal(List<Edge> face)
      {
         Edge eg0 = face[0];
         Edge eg1 = face[1];

         //get two lines from the face
         List<XYZ> points = eg0.Tessellate() as List<XYZ>;
         Autodesk.Revit.DB.XYZ start = points[0];
         Autodesk.Revit.DB.XYZ end = points[1];

         Vector4 vStart = new Vector4((float)start.X, (float)start.Y, (float)start.Z);
         Vector4 vEnd = new Vector4((float)end.X, (float)end.Y, (float)end.Z);
         Vector4 vSub = vEnd - vStart;

         points = eg1.Tessellate() as List<XYZ>;
         start = points[0];
         end = points[1];

         vStart = new Vector4((float)start.X, (float)start.Y, (float)start.Z);
         vEnd = new Vector4((float)end.X, (float)end.Y, (float)end.Z);
         Vector4 vSub2 = vEnd - vStart;

         //get the normal with two lines got from face
         Vector4 result = vSub.CrossProduct(vSub2);
         result.Normalize();
         return result;
      }

      /// <summary>
      /// Get a matrix which can move points to center
      /// </summary>
      /// <returns>matrix used to move point to center of graphics</returns>
      public Matrix4 ToCenterMatrix()
      {
         //translate the origin to bound center
         PointF[] bounds = GetFaceBounds();
         PointF min = bounds[0];
         PointF max = bounds[1];
         PointF center = new PointF((min.X + max.X) / 2, (min.Y + max.Y) / 2);
         return new Matrix4(new Vector4(center.X, center.Y, 0));
      }

      /// <summary>
      /// Get the bound of a face
      /// </summary>
      /// <returns>points array store the bound of the face</returns>
      public PointF[] GetFaceBounds()
      {
         Matrix4 matrix = m_to2DMatrix;
         Matrix4 inverseMatrix = matrix.Inverse();
         float minX = 0, maxX = 0, minY = 0, maxY = 0;
         bool bFirstPoint = true;

         //get the max and min point on the face
         for (int i = 0; i < m_points.Count; i++)
         {
            List<XYZ> points = m_points[i];
            foreach (Autodesk.Revit.DB.XYZ point in points)
            {
               Vector4 v = new Vector4(point);
               Vector4 v1 = inverseMatrix.Transform(v);

               if (bFirstPoint)
               {
                  minX = maxX = v1.X;
                  minY = maxY = v1.Y;
                  bFirstPoint = false;
               }
               else
               {
                  if (v1.X < minX)
                  {
                     minX = v1.X;
                  }
                  else if (v1.X > maxX)
                  {
                     maxX = v1.X;
                  }

                  if (v1.Y < minY)
                  {
                     minY = v1.Y;
                  }
                  else if (v1.Y > maxY)
                  {
                     maxY = v1.Y;
                  }
               }
            }
         }
         //return an array with max and min value of face
         PointF[] resultPoints = new PointF[2] { 
                new PointF(minX, minY), new PointF(maxX, maxY) };
         return resultPoints;
      }
   }
}

LineTool.cs

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

namespace Revit.SDK.Samples.NewPathReinforcement.CS
{
    /// <summary>
    /// tool used to draw line
    /// </summary>
    public class LineTool
    {
        # region members
        private List<Point> m_points = new List<Point>();  // Field used to store points of a line
        private Pen m_backGroundPen;  // background pen used to Erase the preview line
        private Pen m_foreGroundPen;  // foreground pen used to draw lines
        private Point m_preMovePoint;  // store the mouse position when mouse move in pictureBox
        private bool m_finished;    // indicate whether user have finished drawing
        #endregion

        /// <summary>
        /// Finished property to define whether curve was finished
        /// </summary>
        public bool Finished
        {
            get
            {
                return m_finished;
            }
            set
            {
                m_finished = value;
            }
        }

        /// <summary>
        /// PointsNumber property to get the number of points stored
        /// </summary>
        public int PointsNumber
        {
            get
            {
                return m_points.Count;
            }
        }

        /// <summary>
        /// default constructor
        /// </summary>
        public LineTool()
        {
            m_backGroundPen = new Pen(System.Drawing.Color.White);
            m_backGroundPen.Width *= 2;
            m_foreGroundPen = new Pen(System.Drawing.Color.Black);
            m_foreGroundPen.Width *= 2;
            m_finished = false;
        }

        /// <summary>
        /// get all lines drawn in pictureBox
        /// </summary>
        public List<Point> GetPoints()
        {
            return m_points;
        }

        /// <summary>
        /// clear all the points in the tool
        /// </summary>
        public void Clear()
        {
            m_points.Clear();
        }

        /// <summary>
        /// draw a line from end point of tool to the location where mouse move
        /// </summary>
        /// <param name="graphic">graphic object, used to draw geometry</param>
        /// <param name="e">mouse event args</param>
        public void OnMouseMove(System.Drawing.Graphics graphic, System.Windows.Forms.MouseEventArgs e)
        {
            if(m_points.Count != 0 && !m_finished)
            {                
                graphic.DrawLine(m_backGroundPen, m_points[m_points.Count - 1], m_preMovePoint);
                m_preMovePoint = e.Location;
                graphic.DrawLine(m_foreGroundPen, m_points[m_points.Count - 1], e.Location);
            }
        }

        /// <summary>
        /// restore the location point where mouse click
        /// </summary>
        /// <param name="e">mouse event args</param>
        public void OnMouseDown(System.Windows.Forms.MouseEventArgs e)
        {
            //when user click right button of mouse, then erase last line
            if (MouseButtons.Right == e.Button && m_points.Count >= 2)
            {
                m_finished = true;
            }

            if (MouseButtons.Left == e.Button && !m_finished)
            {
                m_preMovePoint = e.Location;
                m_points.Add(e.Location);
            }
        }

        /// <summary>
        /// draw lines stored in the tool
        /// </summary>
        /// <param name="graphic">Graphics object, use to draw geometry</param>
        public void Draw(Graphics graphic)
        {
            for (int i = 0; i < m_points.Count - 1; i++)
            {
                graphic.DrawLine(m_foreGroundPen, m_points[i], m_points[i + 1]);
            }
        }
    }
}

MathTools.cs

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

namespace Revit.SDK.Samples.NewPathReinforcement.CS
{
    /// <summary>
    /// Vector4 class use to store vector
    /// and contain method to handle the vector
    /// </summary>
    public class Vector4
    {
        #region Class member variables and properties
        private float    m_x; 
        private float    m_y; 
        private float    m_z; 
        private float    m_w = 1.0f;

        /// <summary>
        /// X property to get/set x value of Vector4
        /// </summary>
        public float X 
        {
            get
            {
                return m_x;
            }
            set
            {
                m_x = value;
            }
        }

        /// <summary>
        /// Y property to get/set y value of Vector4
        /// </summary>
        public float Y
        {
            get
            {
                return m_y;
            }
            set
            {
                m_y = value;
            }
        }

        /// <summary>
        /// Z property to get/set z value of Vector4
        /// </summary>
        public float Z
        {
            get
            {
                return m_z;
            }
            set
            {
                m_z = value;
            }
        }

        /// <summary>
        /// W property to get/set fourth value of Vector4
        /// </summary>
        public float W
        {
            get
            {
                return m_w;
            }
            set
            {
                m_w = value;
            }
        }
        #endregion

        /// <summary>
        /// constructor
        /// </summary>
        public Vector4(float x, float y, float z)
        {
            this.X = x; this.Y = y; this.Z = z;
        }

        /// <summary>
        /// constructor, transfer Autodesk.Revit.DB.XYZ to vector
        /// </summary>
        /// <param name="v">Autodesk.Revit.DB.XYZ structure which need to be transferred</param>
        public Vector4(Autodesk.Revit.DB.XYZ v)
        {
            this.X = (float)v.X; this.Y = (float)v.Y; this.Z = (float)v.Z;
        }

        /// <summary>
        /// adds two vectors
        /// </summary>
        /// <param name="va">first vector</param>
        /// <param name="vb">second vector</param>
        public static Vector4 operator+ (Vector4 va, Vector4 vb)
        {
            return new Vector4(va.X + vb.X, va.Y + vb.Y, va.Z + vb.Z);
        }

        /// <summary>
        /// subtracts two vectors
        /// </summary>
        /// <param name="va">first vector</param>
        /// <param name="vb">second vector</param>
        /// <returns>subtraction of two vector</returns>
        public static Vector4 operator- (Vector4 va, Vector4 vb)
        {
            return new Vector4(va.X - vb.X, va.Y - vb.Y, va.Z - vb.Z);
        }

        /// <summary>
        /// multiplies a vector by a floating type value
        /// </summary>
        /// <param name="v">vector</param>
        /// <param name="factor">multiplier of floating type</param>
        /// <returns> the result vector </returns>
        public static Vector4 operator* (Vector4 v,float factor)
        {
            return new Vector4(v.X * factor, v.Y * factor, v.Z * factor);
        }

        /// <summary>
        /// divides vector by an floating type value
        /// </summary>
        /// <param name="v">vector</param>
        /// <param name="factor">floating type value</param>
        /// <returns> vector divided by a floating type value </returns>
        public static Vector4 operator /(Vector4 v, float factor)
        {
            return new Vector4(v.X / factor, v.Y / factor, v.Z / factor);
        }

        /// <summary>
        /// dot multiply vector
        /// </summary>
        /// <param name="v"> the result vector </param>
        public float DotProduct(Vector4 v)
        {
            return (this.X * v.X + this.Y * v.Y + this.Z * v.Z);
        }

        /// <summary>
        /// get normal vector of two vectors
        /// </summary>
        /// <param name="v">second vector</param>
        /// <returns> normal vector of two vectors</returns>
        public Vector4 CrossProduct(Vector4 v)
        {
            return new Vector4(this.Y * v.Z - this.Z * v.Y,this.Z * v.X
                - this.X * v.Z,this.X * v.Y - this.Y * v.X);
        }

        /// <summary>
        /// dot multiply two vectors
        /// </summary>
        /// <param name="va">first vector</param>
        /// <param name="vb">second vector</param>
        public static float DotProduct(Vector4 va, Vector4 vb)
        {
            return (va.X * vb.X + va.Y * vb.Y + va.Z * vb.Z);
        }

        /// <summary>
        /// get normal vector of two vectors
        /// </summary>
        /// <param name="va">first vector</param>
        /// <param name="vb">second vector</param>
        /// <returns> normal vector of two vectors </returns>
        public static Vector4 CrossProduct(Vector4 va, Vector4 vb)
        {
            return new Vector4(va.Y * vb.Z - va.Z * vb.Y, va.Z * vb.X
                - va.X * vb.Z, va.X * vb.Y - va.Y * vb.X);
        }

        /// <summary>
        /// get unit vector
        /// </summary>
        public void Normalize()
        {
            float length = Length();
            if(length == 0)
            {
                length = 1;
            }
            this.X /= length;
            this.Y /= length;
            this.Z /= length;
        }

        /// <summary>
        /// calculate the length of vector
        /// </summary>
        public float Length()
        {
            return (float)Math.Sqrt(this.X * this.X + this.Y * this.Y + this.Z * this.Z);
        }
    };

    /// <summary>
    /// Matrix used to transform between ucs coordinate and world coordinate.
    /// </summary>
    public class Matrix4
    {
        #region MatrixType
        /// <summary>
        /// Matrix Type Enum use to define function of matrix
        /// </summary>
        public enum MatrixType
        {
            /// <summary>
            /// matrix use to rotate
            /// </summary>
            Rotation,

            /// <summary>
            /// matrix used to Translation 
            /// </summary>
            Translation,  

            /// <summary>
            /// matrix used to Scale 
            /// </summary>
            Scale,  

            /// <summary>
            /// matrix used to Rotation and Translation 
            /// </summary>
            RotationAndTranslation,

            /// <summary>
            /// normal matrix
            /// </summary>
            Normal 
        };
        private float[,] m_matrix = new float[4,4];
        private MatrixType m_type;
        #endregion

        /// <summary>
        /// default ctor
        /// </summary>
        public Matrix4()
        {
            m_type = MatrixType.Normal;
            Identity();            
        }

        /// <summary>
        /// ctor,rotation matrix,origin at (0,0,0)
        /// </summary>
        /// <param name="xAxis">identity of x axis</param>
        /// <param name="yAxis">identity of y axis</param>
        /// <param name="zAxis">identity of z axis</param>
        public Matrix4(Vector4 xAxis,Vector4 yAxis, Vector4 zAxis)
        {
            m_type = MatrixType.Rotation;
            Identity();
            m_matrix[0, 0] = xAxis.X; m_matrix[0, 1] = xAxis.Y; m_matrix[0, 2] = xAxis.Z;
            m_matrix[1, 0] = yAxis.X; m_matrix[1, 1] = yAxis.Y; m_matrix[1, 2] = yAxis.Z;
            m_matrix[2, 0] = zAxis.X; m_matrix[2, 1] = zAxis.Y; m_matrix[2, 2] = zAxis.Z;
            
        }

        /// <summary>
        /// ctor,translation matrix.
        /// </summary>
        /// <param name="origin">origin of ucs in world coordinate</param>
        public Matrix4(Vector4 origin)
        {
            m_type = MatrixType.Translation;
            Identity();
            m_matrix[3, 0] = origin.X; m_matrix[3, 1] = origin.Y; m_matrix[3, 2] = origin.Z;
        }

        /// <summary>
        /// rotation and translation matrix constructor
        /// </summary>
        /// <param name="xAxis">x Axis</param>
        /// <param name="yAxis">y Axis</param>
        /// <param name="zAxis">z Axis</param>
        /// <param name="origin">origin</param>
        public Matrix4(Vector4 xAxis, Vector4 yAxis, Vector4 zAxis, Vector4 origin)
        {
            m_type = MatrixType.RotationAndTranslation;
            Identity();
            m_matrix[0, 0] = xAxis.X;  m_matrix[0, 1] = xAxis.Y;  m_matrix[0, 2] = xAxis.Z;
            m_matrix[1, 0] = yAxis.X;  m_matrix[1, 1] = yAxis.Y;  m_matrix[1, 2] = yAxis.Z;
            m_matrix[2, 0] = zAxis.X;  m_matrix[2, 1] = zAxis.Y;  m_matrix[2, 2] = zAxis.Z;
            m_matrix[3, 0] = origin.X; m_matrix[3, 1] = origin.Y; m_matrix[3, 2] = origin.Z;
        }

        /// <summary>
        /// scale matrix constructor
        /// </summary>
        /// <param name="scale">scale factor</param>
        public Matrix4(float scale)
        {
            m_type = MatrixType.Scale;
            Identity();
            m_matrix[0, 0] = m_matrix[1, 1] = m_matrix[2, 2] = scale;
        }

        /// <summary>
        /// indexer of matrix
        /// </summary>
        /// <param name="row">row number</param>
        /// <param name="column">column number</param>
        /// <returns></returns>
        public float this[int row, int column]
        {
            get
            {
                return this.m_matrix[row, column];
            }
            set
            {
                this.m_matrix[row, column] = value;
            }
        }

        /// <summary>
        /// Identity matrix
        /// </summary>
        public void Identity()
        {
            for (int i = 0; i < 4; i++)
            {
                for (int j = 0; j < 4; j++)
                {
                    this.m_matrix[i, j] = 0.0f;
                }
            }
            this.m_matrix[0, 0] = 1.0f;
            this.m_matrix[1, 1] = 1.0f;
            this.m_matrix[2, 2] = 1.0f;
            this.m_matrix[3, 3] = 1.0f;
        }

        /// <summary>
        ///  multiply matrix left and right
        /// </summary>
        /// <param name="left">left matrix</param>
        /// <param name="right">right matrix</param>
        /// <returns></returns>
        public static Matrix4 Multiply(Matrix4 left, Matrix4 right)
        {
            Matrix4 result = new Matrix4();
            for (int i = 0; i < 4; i++)
            {
                for (int j = 0; j < 4; j++)
                {
                    result[i, j] = left[i, 0] * right[0, j] + left[i, 1] * right[1, j]
                        + left[i, 2] * right[2, j] + left[i, 3] * right[3, j];
                }
            }
            return result;
        }

        /// <summary>
        /// transform point use this matrix
        /// </summary>
        /// <param name="point">point needed to be transformed</param>
        /// <returns>transform result</returns>
        public Vector4 Transform(Vector4  point)
        {
            return new Vector4(point.X * this[0, 0] + point.Y * this[1, 0]
                + point.Z * this[2, 0]+ point.W * this[3, 0],
                point.X * this[0, 1] + point.Y * this[1, 1]
                + point.Z * this[2, 1]+ point.W * this[3, 1],
                point.X * this[0, 2] + point.Y * this[1, 2]
                + point.Z * this[2, 2]+ point.W * this[3, 2]);
        }

        /// <summary>
        /// if m_matrix is a rotation matrix,this method can get the rotation inverse matrix.
        /// </summary>
        /// <returns>rotation inverse matrix</returns>
        public Matrix4 RotationInverse()
        {
            return new Matrix4(new Vector4(this[0, 0], this[1, 0], this[2, 0]), 
                new Vector4(this[0, 1], this[1, 1], this[2, 1]), 
                new Vector4(this[0, 2], this[1, 2], this[2, 2]));
        }

        /// <summary>
        /// if this m_matrix is a translation matrix,
        /// this method can get the translation inverse matrix.
        /// </summary>
        /// <returns>translation inverse matrix</returns>
        public Matrix4 TranslationInverse()
        {
            return new Matrix4(new Vector4(-this[3, 0], -this[3, 1], -this[3, 2]));
        } 
       
        /// <summary>
        /// get inverse matrix
        /// </summary>
        /// <returns>inverse matrix</returns>
        public Matrix4 Inverse()
        {
            switch(m_type)
            {
                case MatrixType.Rotation:
                    return RotationInverse();

                case MatrixType.Translation:
                    return TranslationInverse();

                case MatrixType.RotationAndTranslation:
                    return Multiply(TranslationInverse(),RotationInverse());
                
                case MatrixType.Scale:
                    return ScaleInverse();
               
                case MatrixType.Normal:
                    return new Matrix4();

                default: return null;
            }
        }

        /// <summary>
        /// if  m_matrix is a scale matrix,this method can get the scale inverse matrix.
        /// </summary>
        /// <returns>scale inverse matrix</returns>
        public Matrix4 ScaleInverse()
        {
            return new Matrix4(1 / m_matrix[0,0]);
        }
    };
}