应用程序:NewOpenings

Revit 平台:所有

Revit 版本:2011.0

首次发布:2008.0

编程语言:C#

技能水平:

类别:元素

类型:ExternalCommand

主题:创建开口。

摘要:

本文介绍了如何通过Revit API创建开窗。

类:

Autodesk.Revit.Creation.Application

Autodesk.Revit.Creation.Document

Autodesk.Revit.DB.Wall

Autodesk.Revit.DB.Floor

Autodesk.Revit.DB.Opening

Autodesk.Revit.DB.XYZ

Autodesk.Revit.DB.Edge

项目文件:

Command.cs

该文件包含了一个实现了IExternalCommand接口的Command类,其功能是显示主要的表单。

 

NewOpeningsForm.cs

该文件包含了一个NewOpeningsForm类。其功能是显示所选墙或地板的外形,并在其上绘制几何图形。

 

Profile.cs

该文件包含了一个抽象类Profile,该类由ProfileWallProfileFloor派生而来。包括两个方法Draw2DDrawOpeningDraw2D用于在表单上绘制外形,DrawOpening用于在所选的墙或地板上创建开口。

 

ITool.cs

该文件包含了一个抽象类ITool,该类由ArcToolNullToolRectToolCircleToolLineTool派生而来。它们用于在NewOpeningsForm上绘制各种几何图形。

 

MathTools.cs

该文件包含了Verctor4Matrix4两个类。这两个类用于将点从3D转换为2D

描述:

本示例使用Document.NewOpening方法在选定的墙壁或地板上创建开口。可以使用ApplicationNewCurveArrayNewArcNewLine方法创建外形。

操作说明:

1. 选择一个墙壁或地板。

2. 运行命令。

3. 在预览面板上绘制开口外形。单击鼠标中键可切换绘制线、矩形、弧形和圆形的工具。

4. 单击“确定”按钮,开口将被创建在墙壁或地板上。

源代码

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

NewOpeningsForm.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.NewOpenings.CS
{
    partial class NewOpeningsForm
    {
        /// <summary>
        /// Required designer variable.
        /// </summary>
        private System.ComponentModel.IContainer components = null;

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

        #region Windows Form Designer generated code

        /// <summary>
        /// Required method for Designer support - do not modify
        /// the contents of this method with the code editor.
        /// </summary>
        private void InitializeComponent()
        {
            this.OkButton = new System.Windows.Forms.Button();
            this.openingPictureBox = new System.Windows.Forms.PictureBox();
            this.cancelButton = new System.Windows.Forms.Button();
            this.Notelabel = new System.Windows.Forms.Label();
            this.Notelabel2 = new System.Windows.Forms.Label();
            ((System.ComponentModel.ISupportInitialize)(this.openingPictureBox)).BeginInit();
            this.SuspendLayout();
            // 
            // OkButton
            // 
            this.OkButton.Location = new System.Drawing.Point(221, 439);
            this.OkButton.Name = "OkButton";
            this.OkButton.Size = new System.Drawing.Size(80, 26);
            this.OkButton.TabIndex = 0;
            this.OkButton.Text = "&OK";
            this.OkButton.UseVisualStyleBackColor = true;
            this.OkButton.Click += new System.EventHandler(this.OkButton_Click);
            // 
            // openingPictureBox
            // 
            this.openingPictureBox.BackColor = System.Drawing.SystemColors.Window;
            this.openingPictureBox.Dock = System.Windows.Forms.DockStyle.Top;
            this.openingPictureBox.Location = new System.Drawing.Point(0, 0);
            this.openingPictureBox.Name = "openingPictureBox";
            this.openingPictureBox.Size = new System.Drawing.Size(415, 381);
            this.openingPictureBox.TabIndex = 1;
            this.openingPictureBox.TabStop = false;
            this.openingPictureBox.MouseDown += new System.Windows.Forms.MouseEventHandler(this.openingPictureBox_MouseDown);
            this.openingPictureBox.MouseMove += new System.Windows.Forms.MouseEventHandler(this.openingPictureBox_MouseMove);
            this.openingPictureBox.Paint += new System.Windows.Forms.PaintEventHandler(this.openingPictureBox_Paint);
            this.openingPictureBox.MouseUp += new System.Windows.Forms.MouseEventHandler(this.openingPictureBox_MouseUp);
            // 
            // cancelButton
            // 
            this.cancelButton.DialogResult = System.Windows.Forms.DialogResult.Cancel;
            this.cancelButton.Location = new System.Drawing.Point(316, 439);
            this.cancelButton.Name = "cancelButton";
            this.cancelButton.Size = new System.Drawing.Size(80, 26);
            this.cancelButton.TabIndex = 1;
            this.cancelButton.Text = "&Cancel";
            this.cancelButton.UseVisualStyleBackColor = true;
            this.cancelButton.Click += new System.EventHandler(this.cancelButton_Click);
            // 
            // Notelabel
            // 
            this.Notelabel.AutoSize = true;
            this.Notelabel.Font = new System.Drawing.Font("Verdana", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
            this.Notelabel.Location = new System.Drawing.Point(-3, 393);
            this.Notelabel.Name = "Notelabel";
            this.Notelabel.Size = new System.Drawing.Size(415, 13);
            this.Notelabel.TabIndex = 2;
            this.Notelabel.Text = "  Use middle button of mouse to switch tool to draw Opening in Preview";
            // 
            // Notelabel2
            // 
            this.Notelabel2.AutoSize = true;
            this.Notelabel2.Font = new System.Drawing.Font("Verdana", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
            this.Notelabel2.Location = new System.Drawing.Point(-3, 416);
            this.Notelabel2.Name = "Notelabel2";
            this.Notelabel2.Size = new System.Drawing.Size(270, 13);
            this.Notelabel2.TabIndex = 3;
            this.Notelabel2.Text = "  Click right button of mouse to close the lines";
            // 
            // NewOpeningsForm
            // 
            this.AcceptButton = this.OkButton;
            this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
            this.BackColor = System.Drawing.SystemColors.Control;
            this.CancelButton = this.cancelButton;
            this.ClientSize = new System.Drawing.Size(415, 473);
            this.Controls.Add(this.Notelabel2);
            this.Controls.Add(this.Notelabel);
            this.Controls.Add(this.cancelButton);
            this.Controls.Add(this.openingPictureBox);
            this.Controls.Add(this.OkButton);
            this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog;
            this.MaximizeBox = false;
            this.MinimizeBox = false;
            this.Name = "NewOpeningsForm";
            this.ShowInTaskbar = false;
            this.Text = "New Openings";
            ((System.ComponentModel.ISupportInitialize)(this.openingPictureBox)).EndInit();
            this.ResumeLayout(false);
            this.PerformLayout();

        }

        #endregion

        private System.Windows.Forms.Button OkButton;
        private System.Windows.Forms.PictureBox openingPictureBox;
        private System.Windows.Forms.Button cancelButton;
        private System.Windows.Forms.Label Notelabel;
        private System.Windows.Forms.Label Notelabel2;

    }
}

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.NewOpenings.CS
{
   /// <summary>
   /// Base class of ProfileFloor and ProfileWall
   /// contain the profile information and can make matrix to transform point to 2D plane
   /// </summary>
   public abstract class Profile
   {
      #region members
      /// <summary>
      ///Wall or Floor element 
      /// </summary>
      protected Autodesk.Revit.DB.Element m_dataProfile;

      /// <summary>
      /// geometry object [face]
      /// </summary>
      protected List<Edge> m_face;

      /// <summary>
      ///  command data
      /// </summary>
      protected Autodesk.Revit.UI.ExternalCommandData m_commandData;

      /// <summary>
      /// Application creator
      /// </summary>
      protected Autodesk.Revit.Creation.Application m_appCreator;

      /// <summary>
      /// Document creator
      /// </summary>
      protected Autodesk.Revit.Creation.Document m_docCreator;
      #endregion

      /// <summary>
      /// Abstract method to create Opening
      /// </summary>
      public abstract void DrawOpening(List<Vector4> points, ToolType type);

      /// <summary>
      /// Draw profile of wall or floor in 2D
      /// </summary>
      /// <param name="graphics">form graphic</param>
      /// <param name="pen">pen use to draw line in pictureBox</param>
      /// <param name="matrix4">matrix used to transform points between 3d and 2d.</param>>
      public void Draw2D(Graphics graphics, Pen pen, Matrix4 matrix4)
      {
         foreach (Edge edge in m_face)
         {
            List<XYZ> points = edge.Tessellate() as List<XYZ>;
            for (int i = 0; i < points.Count - 1; i++)
            {
               Autodesk.Revit.DB.XYZ point1 = points[i];
               Autodesk.Revit.DB.XYZ point2 = points[i + 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>
      /// Constructor
      /// </summary>
      /// <param name="elem">Selected element</param>
      /// <param name="commandData">ExternalCommandData</param>
      public Profile(Autodesk.Revit.DB.Element elem, ExternalCommandData commandData)
      {
         m_dataProfile = elem;
         m_commandData = commandData;
         m_appCreator = m_commandData.Application.Application.Create;
         m_docCreator = m_commandData.Application.ActiveUIDocument.Document.Create;

         List<List<Edge>> faces = GetFaces(m_dataProfile);
         m_face = GetNeedFace(faces);
      }

      /// <summary>
      /// Get edges of element's profile
      /// </summary>
      /// <param name="elem">Selected element</param>
      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;
         Autodesk.Revit.DB.GeometryElement geoElem = elem.get_Geometry(options);

         //GeometryObjectArray gObjects = geoElem.Objects;
         IEnumerator<GeometryObject> Objects = geoElem.GetEnumerator();
         //foreach (GeometryObject geo in gObjects)
         while (Objects.MoveNext())
         {
            GeometryObject geo = Objects.Current;

            Solid solid = geo as Solid;
            if (solid != null)
            {
               EdgeArray edges = solid.Edges;
               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 Face Normal
      /// </summary>
      /// <param name="face">Edges in a face</param>
      private Vector4 GetFaceNormal(List<Edge> face)
      {
         Edge eg0 = face[0];
         Edge eg1 = face[1];

         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;

         Vector4 result = vSub.CrossProduct(vSub2);
         result.Normalize();
         return result;
      }

      /// <summary>
      /// Get First Face
      /// </summary>
      /// <param name="faces">edges in all faces</param>
      private List<Edge> GetNeedFace(List<List<Edge>> faces)
      {
         if (m_dataProfile is Wall)
         {
            return GetWallFace(faces);
         }
         return faces[0];
      }

      /// <summary>
      /// Get a matrix which can transform points to 2D
      /// </summary>
      public Matrix4 To2DMatrix()
      {
         if (m_dataProfile is Wall)
         {
            return WallMatrix();
         }
         List<XYZ> eg0 = m_face[0].Tessellate() as List<XYZ>;
         List<XYZ> eg1 = m_face[1].Tessellate() as List<XYZ>;

         Vector4 v1 = new Vector4((float)eg0[0].X,
             (float)eg0[0].Y, (float)eg0[0].Z);

         Vector4 v2 = new Vector4((float)eg0[1].X,
             (float)eg0[1].Y, (float)eg0[1].Z);
         Vector4 v21 = v1 - v2;
         v21.Normalize();

         Vector4 v3 = new Vector4((float)eg1[0].X,
             (float)eg1[0].Y, (float)eg1[0].Z);

         Vector4 v4 = new Vector4((float)eg1[1].X,
             (float)eg1[1].Y, (float)eg1[1].Z);
         Vector4 v43 = v4 - v3;
         v43.Normalize();

         Vector4 vZAxis = Vector4.CrossProduct(v43, v21);
         Vector4 vYAxis = Vector4.CrossProduct(vZAxis, v43);
         vYAxis.Normalize();
         vZAxis.Normalize();
         Vector4 vOrigin = (v4 + v1) / 2;

         Matrix4 result = new Matrix4(v43, vYAxis, vZAxis, vOrigin);
         return result;
      }

      /// <summary>
      /// Wall matrix
      /// </summary>
      /// <returns></returns>
      public Matrix4 WallMatrix()
      {
         //get the location curve
         LocationCurve location = m_dataProfile.Location as LocationCurve;
         Vector4 xAxis = new Vector4(1, 0, 0);
         Vector4 yAxis = new Vector4(0, 1, 0);
         Vector4 zAxis = new Vector4(0, 0, 1);
         Vector4 origin = new Vector4(0, 0, 0);
         if (location != null)
         {
            Curve curve = location.Curve;
            Autodesk.Revit.DB.XYZ start = curve.GetEndPoint(0);
            Autodesk.Revit.DB.XYZ end = curve.GetEndPoint(1);

            xAxis = new Vector4((float)(end.X - start.X),
                (float)(end.Y - start.Y), (float)(end.Z - start.Z));
            xAxis.Normalize();

            yAxis = new Vector4(0, 0, 1);

            zAxis = Vector4.CrossProduct(xAxis, yAxis);
            zAxis.Normalize();

            origin = new Vector4((float)(end.X + start.X) / 2,
                (float)(end.Y + start.Y) / 2, (float)(end.Z + start.Z) / 2);
         }
         return new Matrix4(xAxis, yAxis, zAxis, origin);
      }

      /// <summary>
      /// Get wall face
      /// </summary>
      /// <param name="faces"></param>
      /// <returns></returns>
      private List<Edge> GetWallFace(List<List<Edge>> faces)
      {
         LocationCurve location = m_dataProfile.Location as LocationCurve;
         Curve curve = location.Curve;
         List<XYZ> xyzs = curve.Tessellate() as List<XYZ>;
         Vector4 zAxis = new Vector4(0, 0, 1);

         if (xyzs.Count == 2)
         {
            return faces[0];
         }

         foreach (List<Edge> face in faces)
         {
            foreach (Edge edge in face)
            {
               List<XYZ> edgexyzs = edge.Tessellate() as List<XYZ>;
               if (xyzs.Count == edgexyzs.Count)
               {
                  Vector4 normal = GetFaceNormal(face);
                  Vector4 cross = Vector4.CrossProduct(zAxis, normal);
                  cross.Normalize();
                  if (cross.Length() == 1)
                  {
                     return face;
                  }
               }
            }
         }
         return faces[0];
      }

      /// <summary>
      /// Get a matrix which can move points to origin
      /// </summary>
      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 Face Bounds
      /// </summary>
      public PointF[] GetFaceBounds()
      {
         Matrix4 matrix = To2DMatrix();
         Matrix4 inverseMatrix = matrix.Inverse();
         float minX = 0, maxX = 0, minY = 0, maxY = 0;
         bool bFirstPoint = true;
         foreach (Edge edge in m_face)
         {
            List<XYZ> points = edge.Tessellate() as List<XYZ>;

            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;
                  }
               }
            }
         }
         PointF[] resultPoints = new PointF[2] { 
                new PointF(minX, minY), new PointF(maxX, maxY) };
         return resultPoints;
      }
   }
}

ITool.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.NewOpenings.CS
{
    /// <summary>
    /// Stand for the draw tool type
    /// </summary>
    public enum ToolType
    {
        /// <summary>
        /// Draw nothing
        /// </summary>
        None,

        /// <summary>
        /// Draw polygon
        /// </summary>
        Line,

        /// <summary>
        /// Draw rectangle
        /// </summary>
        Rectangle,

        /// <summary>
        /// Draw circle
        /// </summary>
        Circle,

        /// <summary>
        /// Draw arc
        /// </summary>
        Arc
    }

    /// <summary>
    /// Abstract class use as base class of all draw tool class
    /// </summary>
    public abstract class ITool
    {
        # region members
        /// <summary>
        /// ToolType is enum type indicate draw tools.
        /// </summary>
        protected ToolType m_type;

        /// <summary>
        /// Field used to store points of a line
        /// </summary>
        protected List<Point> m_points = new List<Point>();
        
        /// <summary>
        /// Field used to store lines
        /// </summary>
        protected List<List<Point>> m_lines = new List<List<Point>>(); 

        /// <summary>
        /// Background pen used to erase the preview line
        /// </summary>
        protected Pen m_backGroundPen;

        /// <summary>
        /// Foreground pen used to draw lines
        /// </summary>
        protected Pen m_foreGroundPen;
  
        /// <summary>
        /// Store the mouse position when mouse move in pictureBox
        /// </summary>        
        protected Point m_preMovePoint;
        #endregion

        /// <summary>
        /// Default constructor
        /// </summary>
        public ITool()
        {
            m_backGroundPen = Pens.White;
            m_foreGroundPen = Pens.Red;
        }

        /// <summary>
        /// Get all lines drawn in pictureBox
        /// </summary>
        public List<List<Point>> GetLines()
        {
            return m_lines;
        }

        /// <summary>
        /// Get the tool type
        /// </summary>
        public virtual ToolType ToolType
        {
            get
            {
                return m_type;
            }
        }

        /// <summary>
        /// Right mouse click event handler  
        /// </summary>
        /// <param name="graphic">Graphics object, used to draw geometry</param>
        /// <param name="e">Mouse event argument</param>
        public virtual void OnRightMouseClick(Graphics graphic, MouseEventArgs e) { }

        /// <summary>
        /// Mouse move event handler
        /// </summary>
        /// <param name="graphic">Graphics object, used to draw geometry</param>
        /// <param name="e">Mouse event argument</param>
        public virtual void OnMouseMove(Graphics graphic, MouseEventArgs e) { }

        /// <summary>
        /// Mouse down event handler
        /// </summary>
        /// <param name="graphic">Graphics object, used to draw geometry</param>
        /// <param name="e">Mouse event argument</param>
        public virtual void OnMouseDown(Graphics graphic, MouseEventArgs e) { }

        /// <summary>
        /// Mouse up event handler
        /// </summary>
        /// <param name="graphic">Graphics object, used to draw geometry</param>
        /// <param name="e">Mouse event argument</param>
        public virtual void OnMouseUp(Graphics graphic, MouseEventArgs e) { }

        /// <summary>
        /// Mouse middle key down event handler
        /// </summary>
        /// <param name="graphic">Graphics object, used to draw geometry</param>
        /// <param name="e">Mouse event argument</param>
        public virtual void OnMidMouseDown(Graphics graphic, MouseEventArgs e)
        {
            this.m_points.Clear();
        }

        /// <summary>
        /// Draw geometries contained in the tool. which class derived from this class
        /// must implement this abstract method
        /// </summary>
        /// <param name="graphic">Graphics object, used to draw geometry</param>
        public abstract void Draw(Graphics graphic);
    }
}

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.NewOpenings.CS
{
    /// <summary>
    /// Vector4 class use to store vector
    /// and contain method to handle the vector
    /// </summary>
    public class Vector4
    {
        #region Member and propertys
        /// <summary>
        /// The coordinate x value
        /// </summary>
        private float    m_x; 
        
        /// <summary>
        /// The coordinate y value
        /// </summary>
        private float    m_y; 

        /// <summary>
        /// The coordinate z value
        /// </summary>
        private float    m_z; 

        /// <summary>
        /// The coordinate w value
        /// </summary>
        private float    m_w = 1.0f;

        /// <summary>
        /// The coordinate x value
        /// </summary>
        public float X 
        {
            get
            {
                return m_x;
            }
            set
            {
                m_x = value;
            }
        }

        /// <summary>
        ///The coordinate y value 
        /// </summary>
        public float Y
        {
            get
            {
                return m_y;
            }
            set
            {
                m_y = value;
            }
        }

        /// <summary>
        /// The coordinate z value
        /// </summary>
        public float Z
        {
            get
            {
                return m_z;
            }
            set
            {
                m_z = value;
            }
        }

        /// <summary>
        /// The coordinate w value
        /// </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>
        /// Copy constructor
        /// </summary>
        public Vector4( Vector4 v)
        {
            this.X = v.X; this.Y = v.Y; this.Z = v.Z;
        }

        /// <summary>
        /// Constructor, transform Autodesk.Revit.DB.XYZ to vector
        /// </summary>
        public Vector4(Autodesk.Revit.DB.XYZ v)
        {
            this.X = (float)v.X; this.Y = (float)v.Y; this.Z = (float)v.Z;
        }

        /// <summary>
        /// Add two vector
        /// </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>
        /// Subtraction of two vector
        /// </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>
        /// Get vector multiply by an double value
        /// </summary>
        /// <param name="v">Vector</param>
        /// <param name="factor">Double value</param>
        /// <returns> Vector multiply by an double value</returns>
        public static Vector4 operator* (Vector4 v,float factor)
        {
            return new Vector4(v.X * factor, v.Y * factor, v.Z * factor);
        }

        /// <summary>
        /// get vector divided by an double value
        /// </summary>
        /// <param name="v">vector</param>
        /// <param name="factor">double value</param>
        /// <returns> vector divided by an double 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">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 vector
        /// </summary>
        /// <param name="v">second vector</param>
        /// <returns> normal vector of two vector</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 vector
        /// </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 vector
        /// </summary>
        /// <param name="va">First vector</param>
        /// <param name="vb">Second vector</param>
        /// <returns> Normal vector of two vector</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 algorithm
        /// </summary>
        public enum MatrixType
        {
            /// <summary>
            /// rotation matrix
            /// </summary>
            Rotation,

            /// <summary>
            /// translation matrix
            /// </summary>
            TransLation,

            /// <summary>
            /// scale matrix
            /// </summary>
            Scale,

            /// <summary>
            /// rotation and translation mix matrix
            /// </summary>
            RotationAndTransLation,

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

        /// <summary>
        /// Construct a identity matrix
        /// </summary>
        public Matrix4()
        {
            m_type = MatrixType.Normal;
            Identity();            
        }

        /// <summary>
        /// Construct a 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 it 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 it 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 it 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]);
        }
    };
}