// // (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.Linq; using Autodesk; using Autodesk.Revit; using Autodesk.Revit.DB; using Autodesk.Revit.UI; using Autodesk.Revit.DB.Structure; using TaskDialog = Autodesk.Revit.UI.TaskDialog; namespace Revit.SDK.Samples.CreateViewSection.CS { /// /// The main class which given a linear element, such as a wall, floor or beam, /// generates a section view across the mid point of the element. /// [Autodesk.Revit.Attributes.Transaction(Autodesk.Revit.Attributes.TransactionMode.Manual)] [Autodesk.Revit.Attributes.Regeneration(Autodesk.Revit.Attributes.RegenerationOption.Manual)] [Autodesk.Revit.Attributes.Journaling(Autodesk.Revit.Attributes.JournalingMode.NoCommandData)] public class Command : IExternalCommand, IDisposable { // Private Members Autodesk.Revit.UI.UIDocument m_project; // Store the current document in revit String m_errorInformation; // Store the error information const Double PRECISION = 0.0000000001; // Define a precision of double data BoundingBoxXYZ m_box; // Store the BoundingBoxXYZ reference used in creation Autodesk.Revit.DB.Element m_currentComponent; // Store the selected element SelectType m_type; // Indicate the type of the selected element. // 0 - wall, 1 - beam, 2 - floor, -1 - invalid const Double LENGTH = 10; // Define half length and width of BoudingBoxXYZ const Double HEIGHT = 5; // Define height of the BoudingBoxXYZ // Define a enum to indicate the selected element type enum SelectType { WALL = 0, BEAM = 1, FLOOR = 2, INVALID = -1 } // Methods /// /// Default constructor of Command /// public Command() { m_type = SelectType.INVALID; } /// /// Implement this method as an external command for Revit. /// /// An object that is passed to the external application /// which contains data related to the command, /// such as the application object and active view. /// A message that can be set by the external application /// which will be displayed if a failure or cancellation is returned by /// the external command. /// A set of elements to which the external application /// can add elements that are to be highlighted in case of failure or cancellation. /// Return the status of the external command. /// A result of Succeeded means that the API external method functioned as expected. /// Cancelled can be used to signify that the user cancelled the external operation /// at some point. Failure should be returned if the application is unable to proceed with /// the operation. public Autodesk.Revit.UI.Result Execute(ExternalCommandData commandData, ref string message, Autodesk.Revit.DB.ElementSet elements) { try { m_project = commandData.Application.ActiveUIDocument; // Get the selected element and store it to data member. if (!GetSelectedElement()) { message = m_errorInformation; return Autodesk.Revit.UI.Result.Failed; } // Create a BoundingBoxXYZ instance which used in NewViewSection() method if (!GenerateBoundingBoxXYZ()) { message = m_errorInformation; return Autodesk.Revit.UI.Result.Failed; } // Create a section view. Transaction transaction = new Transaction(m_project.Document, "CreateSectionView"); transaction.Start(); //ViewSection section = m_project.Document.Create.NewViewSection(m_box); ElementId DetailViewId = ElementId.InvalidElementId; IList elems = new FilteredElementCollector(m_project.Document).OfClass(typeof(ViewFamilyType)).ToElements(); foreach (Element e in elems) { ViewFamilyType v = e as ViewFamilyType; if (v != null && v.ViewFamily == ViewFamily.Detail) { DetailViewId = e.Id; break; } } ViewSection section = ViewSection.CreateDetail(m_project.Document, DetailViewId, m_box); if (null == section) { message = "Can't create the ViewSection."; return Autodesk.Revit.UI.Result.Failed; } // Modify some parameters to make it look better. section.get_Parameter(BuiltInParameter.VIEW_DETAIL_LEVEL).Set(2); transaction.Commit(); // If everything goes right, give successful information and return succeeded. TaskDialog.Show("Revit", "Create view section succeeded."); return Autodesk.Revit.UI.Result.Succeeded; } catch (Exception e) { message = e.Message; return Autodesk.Revit.UI.Result.Failed; } } /// /// Get the selected element, and check whether it is a wall, a floor or a beam. /// /// true if the process succeed; otherwise, false. Boolean GetSelectedElement() { // First get the selection, and make sure only one element in it. ElementSet collection = new ElementSet(); foreach (ElementId elementId in m_project.Selection.GetElementIds()) { collection.Insert(m_project.Document.GetElement(elementId)); } if (1 != collection.Size) { m_errorInformation = "Please select only one element, such as a wall, a beam or a floor."; return false; } // Get the selected element. foreach (Autodesk.Revit.DB.Element e in collection) { m_currentComponent = e; } // Make sure the element to be a wall, beam or a floor. if (m_currentComponent is Wall) { // Check whether the wall is a linear wall LocationCurve location = m_currentComponent.Location as LocationCurve; if (null == location) { m_errorInformation = "The selected wall should be linear."; return false; } if (location.Curve is Line) { m_type = SelectType.WALL; // when the element is a linear wall return true; } else { m_errorInformation = "The selected wall should be linear."; return false; } } FamilyInstance beam = m_currentComponent as FamilyInstance; if (null != beam && StructuralType.Beam == beam.StructuralType) { m_type = SelectType.BEAM; // when the element is a beam return true; } if (m_currentComponent is Floor) { m_type = SelectType.FLOOR; // when the element is a floor. return true; } // If it is not a wall, a beam or a floor, give error information. m_errorInformation = "Please select an element, such as a wall, a beam or a floor."; return false; } /// /// Generate a BoundingBoxXYZ instance which used in NewViewSection() method /// /// true if the instance can be created; otherwise, false. Boolean GenerateBoundingBoxXYZ() { Transaction transaction = new Transaction(m_project.Document, "GenerateBoundingBox"); transaction.Start(); // First new a BoundingBoxXYZ, and set the MAX and Min property. m_box = new BoundingBoxXYZ(); m_box.Enabled = true; Autodesk.Revit.DB.XYZ maxPoint = new Autodesk.Revit.DB.XYZ(LENGTH, LENGTH, 0); Autodesk.Revit.DB.XYZ minPoint = new Autodesk.Revit.DB.XYZ(-LENGTH, -LENGTH, -HEIGHT); m_box.Max = maxPoint; m_box.Min = minPoint; // Set Transform property is the most important thing. // It define the Orgin and the directions(include RightDirection, // UpDirection and ViewDirection) of the created view. Transform transform = GenerateTransform(); if (null == transform) { return false; } m_box.Transform = transform; transaction.Commit(); // If all went well, return true. return true; } /// /// Generate a Transform instance which as Transform property of BoundingBoxXYZ /// /// the reference of Transform, return null if it can't be generated Transform GenerateTransform() { // Because different element have different ways to create Transform. // So, this method just call corresponding method. if (SelectType.WALL == m_type) { return GenerateWallTransform(); } else if (SelectType.BEAM == m_type) { return GenerateBeamTransform(); } else if (SelectType.FLOOR == m_type) { return GenerateFloorTransform(); } else { m_errorInformation = "The program should never go here."; return null; } } /// /// Generate a Transform instance which as Transform property of BoundingBoxXYZ, /// when the user select a wall, this method will be called /// /// the reference of Transform, return null if it can't be generated Transform GenerateWallTransform() { Transform transform = null; Wall wall = m_currentComponent as Wall; // Because the architecture wall and curtain wall don't have analytical Model lines. // So Use Location property of wall object is better choice. // First get the location line of the wall LocationCurve location = wall.Location as LocationCurve; Line locationLine = location.Curve as Line; transform = Transform.Identity; // Second find the middle point of the wall and set it as Origin property. XYZ mPoint = XYZMath.FindMidPoint(locationLine.GetEndPoint(0), locationLine.GetEndPoint(1)); // midPoint is mid point of the wall location, but not the wall's. // The different is the elevation of the point. Then change it. Autodesk.Revit.DB.XYZ midPoint = new XYZ(mPoint.X, mPoint.Y, mPoint.Z + GetWallMidOffsetFromLocation(wall)); transform.Origin = midPoint; // At last find out the directions of the created view, and set it as Basis property. Autodesk.Revit.DB.XYZ basisZ = XYZMath.FindDirection(locationLine.GetEndPoint(0), locationLine.GetEndPoint(1)); Autodesk.Revit.DB.XYZ basisX = XYZMath.FindRightDirection(basisZ); Autodesk.Revit.DB.XYZ basisY = XYZMath.FindUpDirection(basisZ); transform.set_Basis(0, basisX); transform.set_Basis(1, basisY); transform.set_Basis(2, basisZ); return transform; } /// /// Generate a Transform instance which as Transform property of BoundingBoxXYZ, /// when the user select a beam, this method will be called /// /// the reference of Transform, return null if it can't be generated Transform GenerateBeamTransform() { Transform transform = null; FamilyInstance instance = m_currentComponent as FamilyInstance; // First check whether the beam is horizontal. // In order to predigest the calculation, only allow it to be horizontal double startOffset = instance.get_Parameter(BuiltInParameter.STRUCTURAL_BEAM_END0_ELEVATION).AsDouble(); double endOffset = instance.get_Parameter(BuiltInParameter.STRUCTURAL_BEAM_END1_ELEVATION).AsDouble(); if (-PRECISION > startOffset - endOffset || PRECISION < startOffset - endOffset) { m_errorInformation = "Please select a horizontal beam."; return transform; } if (!(instance.Location is LocationCurve)) { m_errorInformation = "The program should never go here."; return transform; } Curve curve = (instance.Location as LocationCurve).Curve; if (null == curve) { m_errorInformation = "The program should never go here."; return transform; } // Now I am sure I can create a transform instance. transform = Transform.Identity; // Third find the middle point of the line and set it as Origin property. Autodesk.Revit.DB.XYZ startPoint = curve.GetEndPoint(0); Autodesk.Revit.DB.XYZ endPoint = curve.GetEndPoint(1); Autodesk.Revit.DB.XYZ midPoint = XYZMath.FindMidPoint(startPoint, endPoint); transform.Origin = midPoint; // At last find out the directions of the created view, and set it as Basis property. Autodesk.Revit.DB.XYZ basisZ = XYZMath.FindDirection(startPoint, endPoint); Autodesk.Revit.DB.XYZ basisX = XYZMath.FindRightDirection(basisZ); Autodesk.Revit.DB.XYZ basisY = XYZMath.FindUpDirection(basisZ); transform.set_Basis(0, basisX); transform.set_Basis(1, basisY); transform.set_Basis(2, basisZ); return transform; } /// /// Generate a Transform instance which as Transform property of BoundingBoxXYZ, /// when the user select a floor, this method will be called /// /// the reference of Transform, return null if it can't be generated Transform GenerateFloorTransform() { Transform transform = null; Floor floor = m_currentComponent as Floor; // First get the Analytical Model lines AnalyticalPanel model = null; Document document = floor.Document; AnalyticalToPhysicalAssociationManager assocManager = AnalyticalToPhysicalAssociationManager.GetAnalyticalToPhysicalAssociationManager(document); if (assocManager != null) { ElementId associatedElementId = assocManager.GetAssociatedElementId(floor.Id); if (associatedElementId != ElementId.InvalidElementId) { Element associatedElement = document.GetElement(associatedElementId); if (associatedElement != null && associatedElement is AnalyticalPanel) { model = associatedElement as AnalyticalPanel; } } } if (null == model) { m_errorInformation = "Please select a structural floor."; return transform; } CurveArray curves = m_project.Document.Application.Create.NewCurveArray(); IList curveList = model.GetOuterContour().ToList(); foreach (Curve curve in curveList) { curves.Append(curve); } if (null == curves || true == curves.IsEmpty) { m_errorInformation = "The program should never go here."; return transform; } // Now I am sure I can create a transform instance. transform = Transform.Identity; // Third find the middle point of the floor and set it as Origin property. Autodesk.Revit.DB.XYZ midPoint = XYZMath.FindMiddlePoint(curves); transform.Origin = midPoint; // At last find out the directions of the created view, and set it as Basis property. Autodesk.Revit.DB.XYZ basisZ = XYZMath.FindFloorViewDirection(curves); Autodesk.Revit.DB.XYZ basisX = XYZMath.FindRightDirection(basisZ); Autodesk.Revit.DB.XYZ basisY = XYZMath.FindUpDirection(basisZ); transform.set_Basis(0, basisX); transform.set_Basis(1, basisY); transform.set_Basis(2, basisZ); return transform; } Double GetWallMidOffsetFromLocation(Wall wall) { // First get the "Base Offset" property. Double baseOffset = wall.get_Parameter(BuiltInParameter.WALL_BASE_OFFSET).AsDouble(); // Second get the "Unconnected Height" property. Double height = wall.get_Parameter(BuiltInParameter.WALL_USER_HEIGHT_PARAM).AsDouble(); // Get the middle of of wall elevation from the wall location. // The elevation of wall location equals the elevation of "Base Constraint" level Double midOffset = baseOffset + height / 2; return midOffset; } protected virtual void Dispose(bool disposing) { if (disposing) { (m_box as IDisposable)?.Dispose(); } } public void Dispose() { Dispose(disposing: true); GC.SuppressFinalize(this); } } /// /// Create a drafting view. /// [Autodesk.Revit.Attributes.Transaction(Autodesk.Revit.Attributes.TransactionMode.Manual)] [Autodesk.Revit.Attributes.Regeneration(Autodesk.Revit.Attributes.RegenerationOption.Manual)] public class CreateDraftingView : IExternalCommand { public Autodesk.Revit.UI.Result Execute( ExternalCommandData commandData, ref string message, ElementSet elements) { try { Autodesk.Revit.DB.Document doc = commandData.Application.ActiveUIDocument.Document; Transaction transaction = new Transaction(doc, "CreateDraftingView"); transaction.Start(); ViewFamilyType viewFamilyType = null; FilteredElementCollector collector = new FilteredElementCollector(doc); var viewFamilyTypes = collector.OfClass(typeof(ViewFamilyType)).ToElements(); foreach (Element e in viewFamilyTypes) { ViewFamilyType v = e as ViewFamilyType; if (v.ViewFamily == ViewFamily.Drafting) { viewFamilyType = v; break; } } ViewDrafting drafting = ViewDrafting.Create(doc, viewFamilyType.Id); if (null == drafting) { message = "Can't create the ViewDrafting."; return Autodesk.Revit.UI.Result.Failed; } transaction.Commit(); TaskDialog.Show("Revit", "Create view drafting succeeded."); return Autodesk.Revit.UI.Result.Succeeded; } catch (Exception e) { message = e.Message; return Autodesk.Revit.UI.Result.Failed; } } } }