应用程序:ShaftHolePuncher

Revit 平台:全部

Revit 版本:2011.0

首次发布于:2008.0

编程语言:C

技能水平:高级

类别:几何、元素

类型:外部命令

主题:创建所有类型的开口。

摘要:

本示例演示如何在墙体、地板或梁上创建单个或轴向开口。

相关类:

Autodesk.Revit.UI.IExternalCommand

Autodesk.Revit.DB.Opening

Autodesk.Revit.DB.FamilyInstance

Autodesk.Revit.DB.Level

Autodesk.Revit.DB.Face

Autodesk.Revit.DB.Edge

Autodesk.Revit.DB.Solid

Autodesk.Revit.DB.Instance

Autodesk.Revit.DB.Transform

Autodesk.Revit.DB.CurveArray

Autodesk.Revit.Creation.Document

项目文件:

Command.cs

该文件包含一个实现了 IExternalCommand 接口的 Command 类,该类获取所选的墙体、地板或梁,并在其上创建开口。

 

ShaftHolePuncherForm.cs

该文件包含一个继承自 Form ShaftHolePuncherForm 类。该类的功能是显示所选墙体、地板或梁的剖面,并允许用户在上面绘制开口的剖面。如果用户想要创建轴向开口,则会显示一个坐标系。

 

Profile.cs

该文件包含一个类 Profile,它派生自 ProfileWallProfileFloorProfileBeam ProfileNullProfile 包括 Draw2D() CreateOpening() 两种方法。Draw2D() 用于在窗体上绘制剖面,而 CreateOpening() 用于在相应的主体上创建开口。

 

ProfileNull.cs

该文件包含一个派生自 Profile ProfileNull 类。ProfileNull 重写了 Draw2D() CreateOpening()Draw2D() 用于绘制坐标系,以帮助用户绘制轴向开口的曲线环,而 CreateOpening() 用于创建轴向开口。

 

ProfileBeam.cs

该文件包含一个派生自 Profile ProfileBeam 类。ProfileBeam 重写了 GetTo2DMatrix()Draw2D() CreateOpening() 方法。Draw2D() 用于绘制梁的剖面(用户可以通过下拉列表框更改方向来创建开口),而 CreateOpening() 用于在梁上创建开口。

 

LineTool.cs

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

 

RectangleTool.cs

该文件包含一个名为 RectangleTool 的类,该类提供了一些在窗体上绘制矩形并存储绘制矩形数据的方法。

 

MathTools.cs

该文件包含 Verctor4 Matrix4 两个类。这两个类用于在 3D 2D 之间转换点。

描述:

该示例展示了用户如何使用 Autodesk.Revit.Creation.Document.NewOpening(...) 方法创建开口,该方法有四个重载:

- NewOpening(Element, CurveArray, eRefFace) 用于在梁、支撑或柱子中创建开口。eRefFace 用于定义开口所基于的面。

- NewOpening(Element, CurveArray, bool) 用于在屋顶、地板或天花板上创建开口,bool 值用于指示是垂直切割还是垂直切割。

- NewOpening(Level, Level, CurveArray) 用于创建轴向开口。

- NewOpening(Wall, ref XYZ, ref XYZ) 用于在墙体上创建开口。

说明:

1.绘制墙、地板或梁并选择它。如果要创建Shaft Opening(Shaft洞开),则不要选择任何内容。 (或打开ShaftHolePuncher.rvt文件)

2.运行此命令。

3.绘制您要创建的开口轮廓,单击鼠标右键以关闭曲线并完成绘制。

4.如果用户要在梁上创建开口,则可以通过组合框改变方向以创建开口。图片框将以相应方向绘制梁的轮廓。

5.如果这是一个Shaft Opening(Shaft洞开),用户可以在显示的坐标系中绘制曲线,并在组合框中选择比例系数以缩放它。该命令将从Level1Level2创建Shaft Opening(Shaft洞开)

6.用户可以通过单击“Clean”按钮来清除并重新绘制开口轮廓。

7.单击“Create”按钮将在Revit中创建Opening(开口),当您完成曲线绘制时。

源代码:

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

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.ShaftHolePuncher.CS
{
    /// <summary>
    /// Abstract class used as a base class of all drawing tool class
    /// </summary>
    public abstract class ITool
    {
        # region members
        protected List<Point> m_points = new List<Point>();  // Field used to store points of a line
        protected Pen m_backGroundPen;  // background pen used to Erase the preview line
        protected Pen m_foreGroundPen;  // foreground pen used to draw lines
        protected Point m_preMovePoint;  // store the mouse position when mouse move in pictureBox
        protected Point m_preDownPoint; // store the mouse position when right mouse button clicked in pictureBox
        protected bool m_finished;    // indicate whether user have finished drawing
        #endregion
        /// <summary>
        /// Finished property to define whether curve was finished
        /// </summary>
        public virtual bool Finished
        {
            get
            {
                return m_finished;
            }
            set
            {
                m_finished = value;
            }
        }
        /// <summary>
        /// get all lines drawn in pictureBox
        /// </summary>
        public virtual List<Point> Points
        {
            get
            {
                return m_points;
            }
        }
        /// <summary>
        /// default constructor
        /// </summary>
        public ITool()
        {
            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>
        /// calculate the distance between two points
        /// </summary>
        /// <param name="p1">first point</param>
        /// <param name="p2">second point</param>
        /// <returns>distance between two points</returns>
        public double GetDistance(Point p1, Point p2)
        {
            double distance = Math.Sqrt(
                (p2.X - p1.X) * (p2.X - p1.X) + (p2.Y - p1.Y) * (p2.Y - p1.Y));
            return distance;
        }
        /// <summary>
        /// clear all the points in the tool
        /// </summary>
        public virtual void Clear() 
        {
            m_points.Clear();
        }
        /// <summary>
        /// draw a line from end point to the location where mouse moved
        /// </summary>
        /// <param name="graphic">Graphics object,used to draw geometry</param>
        /// <param name="e">mouse event args</param>
        public virtual void OnMouseMove(System.Drawing.Graphics graphic, 
            System.Windows.Forms.MouseEventArgs e) { }
        /// <summary>
        /// record the location point where mouse clicked
        /// </summary>
        /// <param name="e">mouse event args</param>
        public virtual void OnMouseDown(System.Windows.Forms.MouseEventArgs e) { }
        /// <summary>
        /// draw the stored lines
        /// </summary>
        /// <param name="graphic">Graphics object, used to draw geometry</param>
        public virtual void Draw(Graphics graphic) { }
    }
}

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.ShaftHolePuncher.CS
{
    /// <summary>
    /// tool used to draw line
    /// </summary>
    public class LineTool : ITool
    {
        /// <summary>
        /// draw a line from end point of tool to the location where mouse moved
        /// </summary>
        /// <param name="graphic">graphic object,used to draw geometry</param>
        /// <param name="e">mouse event args</param>
        public override 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>
        /// record the location point where mouse clicked
        /// </summary>
        /// <param name="e">mouse event args</param>
        public override void OnMouseDown(System.Windows.Forms.MouseEventArgs e)
        {
            //when user click right button of mouse,
            //finish the curve if the number of points is more than 2
            if (MouseButtons.Right == e.Button && m_points.Count > 2)
            {
                m_finished = true;
            }
            if (MouseButtons.Left == e.Button && !m_finished
                && GetDistance(m_preDownPoint, e.Location) > 2)
            {
                m_preDownPoint = e.Location;
                m_points.Add(e.Location);
            }
        }
        /// <summary>
        /// draw lines recorded in the tool
        /// </summary>
        /// <param name="graphic">Graphics object, use to draw geometry</param>
        public override 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]);
            }
            //if user finished draw (clicked the right button), then close the curve
            if (m_finished)
            {
                graphic.DrawLine(m_foreGroundPen, m_points[0], m_points[m_points.Count - 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.ShaftHolePuncher.CS
{
/// <summary>
/// Vector4 class used 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 needs 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 vectors</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] = scale;
m_matrix[1, 1] = scale;
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 using this matrix
/// </summary>
/// <param name="point">point 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]);
}
};
}

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.ShaftHolePuncher.CS
{
/// <summary>
/// base class of ProfileFloor, ProfileWall and ProfileNull.
/// contains the profile information and can calculate matrix to transform point to 2D plane
/// </summary>
public abstract class Profile
{
#region class member variables
// store all the points on the needed face
protected List<List<XYZ>> m_points;

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

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

// used to create new instances of elements
protected Autodesk.Revit.Creation.Document m_docCreator;

// store the Matrix used to transform 3D points to 2D
protected Matrix4 m_to2DMatrix = null;

// store the Matrix used to move points to center
protected Matrix4 m_moveToCenterMatrix = null;

// store the Matrix used to scale profile fit to pictureBox
protected Matrix4 m_scaleMatrix = null;

// store the Matrix used to transform Revit coordinate to window UI
protected Matrix4 m_transformMatrix = null;

// store the Matrix used to transform window UI coordinate to Revit
protected Matrix4 m_restoreMatrix = null;

// store the size of pictureBox in UI
protected Size m_sizePictureBox;
#endregion

/// <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_docCreator = m_commandData.Application.ActiveUIDocument.Document.Create;
}

/// <summary>
/// abstract method to create Opening
/// </summary>
/// <returns>newly created Opening</returns>
/// <param name="points">points used to create Opening</param>
public abstract Opening CreateOpening(List<Vector4> points);

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

/// <summary>
/// Get a matrix which can transform points to 2D
/// </summary>
public virtual Matrix4 GetTo2DMatrix()
{
return null;
}

/// <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 virtual void Draw2D(Graphics graphics, Pen pen, Matrix4 matrix4)
{
//move the gdi origin to the picture center
graphics.Transform = new System.Drawing.Drawing2D.Matrix(
1, 0, 0, 1, m_sizePictureBox.Width / 2, m_sizePictureBox.Height / 2);

//draw profile
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 virtual 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;
//make sure references to geometric objects are computed.
options.ComputeReferences = true;
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 stores the bound of the face</returns>
public virtual 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;
}

/// <summary>
/// calculate the matrix use to scale
/// </summary>
/// <param name="size">pictureBox size</param>
/// <returns>maxtrix is use to scale the profile</returns>
public virtual Matrix4 ComputeScaleMatrix(Size size)
{
m_sizePictureBox = size;
PointF[] boundPoints = GetFaceBounds();
float width = ((float)size.Width) / (boundPoints[1].X - boundPoints[0].X);
float hight = ((float)size.Height) / (boundPoints[1].Y - boundPoints[0].Y);
float factor = width <= hight ? width : hight;
//leave some margin, so multiply factor by 0.85
m_scaleMatrix = new Matrix4((float)(factor * 0.85));
return m_scaleMatrix;
}

/// <summary>
/// calculate the matrix used to transform 3D to 2D
/// </summary>
/// <returns>maxtrix is use to transform 3d points to 2d</returns>
public virtual Matrix4 Compute3DTo2DMatrix()
{
Matrix4 result = Matrix4.Multiply(
m_to2DMatrix.Inverse(), m_moveToCenterMatrix.Inverse());
m_transformMatrix = Matrix4.Multiply(result, m_scaleMatrix);
return m_transformMatrix;
}

/// <summary>
/// transform the point on Form to 3d world coordinate of revit
/// </summary>
/// <param name="ps">contain the points to be transformed</param>
/// <returns>Vector list contains points being transformed</returns>
public virtual List<Vector4> Transform2DTo3D(Point[] ps)
{
List<Vector4> result = new List<Vector4>();
TransformPoints(ps);
Matrix4 transformMatrix = Matrix4.Multiply(
m_scaleMatrix.Inverse(), m_moveToCenterMatrix);
transformMatrix = Matrix4.Multiply(transformMatrix, m_to2DMatrix);
foreach (Point point in ps)
{
Vector4 v = new Vector4(point.X, point.Y, 0);
v = transformMatrix.Transform(v);
result.Add(v);
}
return result;
}

/// <summary>
/// use matrix to transform point
/// </summary>
/// <param name="pts">contain the points to be transformed</param>
private void TransformPoints(Point[] pts)
{
System.Drawing.Drawing2D.Matrix matrix = new System.Drawing.Drawing2D.Matrix(
1, 0, 0, 1, m_sizePictureBox.Width / 2, m_sizePictureBox.Height / 2);
matrix.Invert();
matrix.TransformPoints(pts);
}
}
}