// // (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 { /// /// base class of ProfileFloor, ProfileWall and ProfileNull. /// contains the profile information and can calculate matrix to transform point to 2D plane /// public abstract class Profile { #region class member variables // store all the points on the needed face protected List> 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 /// /// constructor /// /// object which contains reference to Revit Application protected Profile(ExternalCommandData commandData) { m_commandData = commandData; m_appCreator = m_commandData.Application.Application.Create; m_docCreator = m_commandData.Application.ActiveUIDocument.Document.Create; } /// /// abstract method to create Opening /// /// newly created Opening /// points used to create Opening public abstract Opening CreateOpening(List points); /// /// Get points in first face /// /// edges in all faces /// points in first face public virtual List> GetNeedPoints(List> faces) { return null; } /// /// Get a matrix which can transform points to 2D /// public virtual Matrix4 GetTo2DMatrix() { return null; } /// /// draw profile of wall or floor in 2D /// /// form graphic /// pen used to draw line in pictureBox /// Matrix used to transform 3d to 2d /// and make picture in right scale 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 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)); } } } /// /// Get edges of element's profile /// /// selected element /// all the faces in the selected Element public virtual List> GetFaces(Autodesk.Revit.DB.Element elem) { List> faceEdges = new List>(); 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 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 edgesList = new List(); foreach (Edge edge in edgeArr) { edgesList.Add(edge); } faceEdges.Add(edgesList); } } } } return faceEdges; } /// /// Get normal of face /// /// edges in a face /// vector stands for normal of the face public Vector4 GetFaceNormal(List face) { Edge eg0 = face[0]; Edge eg1 = face[1]; //get two lines from the face List points = eg0.Tessellate() as List; 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; 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; } /// /// Get a matrix which can move points to center /// /// matrix used to move point to center of graphics 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)); } /// /// Get the bound of a face /// /// points array stores the bound of the face 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 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; } /// /// calculate the matrix use to scale /// /// pictureBox size /// maxtrix is use to scale the profile 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; } /// /// calculate the matrix used to transform 3D to 2D /// /// maxtrix is use to transform 3d points to 2d public virtual Matrix4 Compute3DTo2DMatrix() { Matrix4 result = Matrix4.Multiply( m_to2DMatrix.Inverse(), m_moveToCenterMatrix.Inverse()); m_transformMatrix = Matrix4.Multiply(result, m_scaleMatrix); return m_transformMatrix; } /// /// transform the point on Form to 3d world coordinate of revit /// /// contain the points to be transformed /// Vector list contains points being transformed public virtual List Transform2DTo3D(Point[] ps) { List result = new List(); 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; } /// /// use matrix to transform point /// /// contain the points to be transformed 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); } } }