// // (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.IO; using System.Diagnostics; using Autodesk.Revit; using Autodesk.Revit.ApplicationServices; using Autodesk.Revit.DB; using Autodesk.Revit.UI; using Autodesk.Revit.DB.Mechanical; using GXYZ = Autodesk.Revit.DB.XYZ; namespace Revit.SDK.Samples.AutoRoute.CS { /// /// route a set of ducts and fittings between a base air supply equipment and 2 terminals. /// [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 { /// /// The revit application /// private static Application m_application; /// /// The current document of the application /// private static Document m_document; /// /// The mechanical system that will be created /// private MechanicalSystem m_mechanicalSystem; /// /// The type of the ducts in the system /// DuctType dtRectangle; /// /// The type id of the duct /// Autodesk.Revit.DB.ElementId ductTypeId = new Autodesk.Revit.DB.ElementId(139191L); /// /// The system type id of the duct /// ElementId systemTypeId = ElementId.InvalidElementId; /// /// The level of the duct /// Level lvl =null; /// /// Minimum length of a fitting /// private const double min1FittingLength = 1; /// /// Minimum length of a duct /// private const double minDuctLength = 0.5; /// /// The vertical offset of the highest connector to the trunk ducts /// private const double verticalTrunkOffset = 15; /// /// Optional distance from trunk to the boundary of system bounding box /// used when failed to lay out ducts in the region of bounding box /// private const double horizontalOptionalTrunkOffset = 5; /// /// Minimum length of 2 fittings /// private const double min2FittingsLength = min1FittingLength * 2; /// /// The minimum length of 1 duct with 2 fittings /// private const double min1Duct2FittingsLength = min1FittingLength * 2 + minDuctLength; #region IExternalCommand Members /// /// 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, ElementSet elements) { // set out default result to Failed. Autodesk.Revit.UI.Result retRes = Autodesk.Revit.UI.Result.Failed; m_application = commandData.Application.Application; m_document = commandData.Application.ActiveUIDocument.Document; Trace.Listeners.Clear(); Trace.AutoFlush = true; //get the system type id of the duct ElementClassFilter systemTypeFilter = new ElementClassFilter(typeof(MEPSystemType)); FilteredElementCollector C = new FilteredElementCollector(m_document); C.WherePasses(systemTypeFilter); foreach (MEPSystemType type in C) { if (type.SystemClassification == MEPSystemClassification.SupplyAir) { systemTypeId = type.Id; break; } } string outputFileName = Path.Combine(Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location), "AutoRoute.log"); if (File.Exists(outputFileName)) { File.Delete(outputFileName); } TextWriterTraceListener listener = new TextWriterTraceListener(outputFileName); Trace.Listeners.Add(listener); Transaction transaction = new Transaction(m_document, "Sample_AutoRoute"); try { transaction.Start(); //set the level of the duct lvl = Level.Create(m_document, 0.0); //Lists to temporarily record the created elements List ducts = new List(); List points = new List(); List connectors = new List(); List baseConnectors = new List(); //Get the connectors and bounding boxes List ids = new List(); ids.Add(new ElementId(378728L)); ids.Add(new ElementId(378707L)); ids.Add(new ElementId(378716L)); FamilyInstance[] instances = new FamilyInstance[3]; Autodesk.Revit.DB.BoundingBoxXYZ[] boxes = new Autodesk.Revit.DB.BoundingBoxXYZ[3]; Connector[] conns = new Connector[3]; ConnectorSetIterator csi = null; for (int i = 0; i < ids.Count; ++i) { Element element = m_document.GetElement(ids[i]); if (null == element) { message = "Element " + ids[i].ToString() + " can't be found."; return Autodesk.Revit.UI.Result.Failed; } instances[i] = element as FamilyInstance; csi = ConnectorInfo.GetConnectors(ids[i]).ForwardIterator(); csi.MoveNext(); conns[i] = csi.Current as Connector; boxes[i] = instances[i].get_BoundingBox(m_document.ActiveView); } //Find the "Out" and "SupplyAir" connector on the base equipment csi = ConnectorInfo.GetConnectors(ids[0]).ForwardIterator(); while (csi.MoveNext()) { Connector conn = csi.Current as Connector; if (conn.Direction == FlowDirectionType.Out && conn.DuctSystemType == DuctSystemType.SupplyAir) { conns[0] = conn; } } //Create a mechanical system with a base air supply equipment and 2 terminals. m_mechanicalSystem = CreateMechanicalSystem( //[378728][SupplyAir][Out][RectProfile][OST_MechanicalEquipment] new ConnectorInfo(new ElementId(378728L), conns[0].Origin.X, conns[0].Origin.Y, conns[0].Origin.Z), new ConnectorInfo[]{ //[378707][SupplyAir][In][RectProfile] new ConnectorInfo(new ElementId(378707L), conns[1].Origin.X, conns[1].Origin.Y, conns[1].Origin.Z), //[378716][SupplyAir][In][RectProfile] new ConnectorInfo(new ElementId(378716L), conns[2].Origin.X, conns[2].Origin.Y, conns[2].Origin.Z) }, DuctSystemType.SupplyAir ); //Get the boundary of the system double minX = conns[0].Origin.X; double minY = conns[0].Origin.Y; double maxX = conns[0].Origin.X; double maxY = conns[0].Origin.Y; double maxZ = conns[0].Origin.Z; for (int i = 1; i < boxes.Length; ++i) { if (conns[i].Origin.X < minX) minX = conns[i].Origin.X; if (conns[i].Origin.Y < minY) minY = conns[i].Origin.Y; if (conns[i].Origin.X > maxX) maxX = conns[i].Origin.X; if (conns[i].Origin.Y > maxY) maxY = conns[i].Origin.Y; if (conns[i].Origin.Z > maxZ) maxZ = conns[i].Origin.Z; } //Calculate the optional values for the trunk ducts double midX = (minX + maxX) / 2; double midY = (minY + maxY) / 2; double[] baseXValues = new double[3] { midX, (minX + midX) / 2, (maxX + midX) / 2 }; double[] baseYValues = new double[3] { midY, (minY + midY) / 2, (maxY + midY) / 2 }; //Get the duct type for the ducts to be created dtRectangle = m_document.GetElement(ductTypeId) as DuctType; //Create the ducts and elbows that connect the base mechanical equipment GXYZ connectorDirection = conns[0].CoordinateSystem.BasisZ; if (0 == connectorDirection.DistanceTo(new GXYZ(-1, 0, 0))) { points.Add(new GXYZ(conns[0].Origin.X - min1FittingLength, conns[0].Origin.Y, conns[0].Origin.Z)); points.Add(new GXYZ(conns[0].Origin.X - min2FittingsLength, conns[0].Origin.Y, conns[0].Origin.Z + min1FittingLength)); points.Add(new GXYZ(conns[0].Origin.X - min2FittingsLength, conns[0].Origin.Y, maxZ + verticalTrunkOffset - min1FittingLength)); } else if (0 == connectorDirection.DistanceTo(new GXYZ(1, 0, 0))) { points.Add(new GXYZ(conns[0].Origin.X + min1FittingLength, conns[0].Origin.Y, conns[0].Origin.Z)); points.Add(new GXYZ(conns[0].Origin.X + min2FittingsLength, conns[0].Origin.Y, conns[0].Origin.Z + min1FittingLength)); points.Add(new GXYZ(conns[0].Origin.X + min2FittingsLength, conns[0].Origin.Y, maxZ + verticalTrunkOffset - min1FittingLength)); } else if (0 == connectorDirection.DistanceTo(new GXYZ(0, -1, 0))) { points.Add(new GXYZ(conns[0].Origin.X, conns[0].Origin.Y - min1FittingLength, conns[0].Origin.Z)); points.Add(new GXYZ(conns[0].Origin.X, conns[0].Origin.Y - min2FittingsLength, conns[0].Origin.Z + min1FittingLength)); points.Add(new GXYZ(conns[0].Origin.X, conns[0].Origin.Y - min2FittingsLength, maxZ + verticalTrunkOffset - min1FittingLength)); } else if (0 == connectorDirection.DistanceTo(new GXYZ(0, 1, 0))) { points.Add(new GXYZ(conns[0].Origin.X, conns[0].Origin.Y + min1FittingLength, conns[0].Origin.Z)); points.Add(new GXYZ(conns[0].Origin.X, conns[0].Origin.Y + min2FittingsLength, conns[0].Origin.Z + min1FittingLength)); points.Add(new GXYZ(conns[0].Origin.X, conns[0].Origin.Y + min2FittingsLength, maxZ + verticalTrunkOffset - min1FittingLength)); } ducts.Add(Duct.Create(m_document, ductTypeId, lvl.Id,conns[0], points[0])); ducts.Add(Duct.Create(m_document, systemTypeId, ductTypeId, lvl.Id, points[1], points[2])); connectors.Add(ConnectorInfo.GetConnector(ducts[0].Id, points[0])); connectors.Add(ConnectorInfo.GetConnector(ducts[1].Id, points[1])); connectors.Add(ConnectorInfo.GetConnector(ducts[1].Id, points[2])); connectors[0].ConnectTo(connectors[1]); m_document.Create.NewElbowFitting(connectors[0], connectors[1]); baseConnectors.Add(connectors[2]); //Create the vertical ducts for terminals points.Clear(); ducts.Clear(); points.Add(new GXYZ(conns[1].Origin.X, conns[1].Origin.Y, maxZ + verticalTrunkOffset - min1FittingLength)); points.Add(new GXYZ(conns[2].Origin.X, conns[2].Origin.Y, maxZ + verticalTrunkOffset - min1FittingLength)); ducts.Add(Duct.Create(m_document, ductTypeId, lvl.Id, conns[1], points[0])); ducts.Add(Duct.Create(m_document, ductTypeId, lvl.Id, conns[2], points[1])); baseConnectors.Add(ConnectorInfo.GetConnector(ducts[0].Id, points[0])); baseConnectors.Add(ConnectorInfo.GetConnector(ducts[1].Id, points[1])); //Connect the system by creating the trunk line of ducts and connect them to the base connectors SortConnectorsByX(baseConnectors); for (int i = 0; i < baseYValues.Length; ++i) { if (ConnectSystemOnXAxis(baseConnectors, baseYValues[i])) { LogUtility.WriteMechanicalSystem(m_mechanicalSystem); return Autodesk.Revit.UI.Result.Succeeded; } } SortConnectorsByY(baseConnectors); for (int i = 0; i < baseXValues.Length; ++i) { if (ConnectSystemOnYAxis(baseConnectors, baseXValues[i])) { LogUtility.WriteMechanicalSystem(m_mechanicalSystem); return Autodesk.Revit.UI.Result.Succeeded; } } //If all the cases fail to route the system, try the trunks out of the bounding box SortConnectorsByX(baseConnectors); if (ConnectSystemOnXAxis(baseConnectors, maxY + horizontalOptionalTrunkOffset)) { LogUtility.WriteMechanicalSystem(m_mechanicalSystem); return Autodesk.Revit.UI.Result.Succeeded; } SortConnectorsByY(baseConnectors); if (ConnectSystemOnYAxis(baseConnectors, maxX + horizontalOptionalTrunkOffset)) { LogUtility.WriteMechanicalSystem(m_mechanicalSystem); return Autodesk.Revit.UI.Result.Succeeded; } //If there's no path for the connection, choose one path and let Revit report the error connectors.Clear(); SortConnectorsByX(baseConnectors); connectors.AddRange(CreateDuct(new GXYZ(baseConnectors[0].Origin.X + min1FittingLength, baseYValues[0], maxZ + verticalTrunkOffset), new GXYZ(baseConnectors[1].Origin.X - min1FittingLength, baseYValues[0], maxZ + verticalTrunkOffset))); connectors.AddRange(CreateDuct(new GXYZ(baseConnectors[1].Origin.X + min1FittingLength, baseYValues[0], maxZ + verticalTrunkOffset), new GXYZ(baseConnectors[2].Origin.X - min1FittingLength, baseYValues[0], maxZ + verticalTrunkOffset))); ConnectWithElbowFittingOnXAxis(baseConnectors[0], connectors[0]); ConnectWithElbowFittingOnXAxis(baseConnectors[2], connectors[3]); ConnectWithTeeFittingOnXAxis(baseConnectors[1], connectors[1], connectors[2], false); } catch (Exception ex) { Trace.WriteLine(ex.ToString()); message = ex.Message; retRes = Autodesk.Revit.UI.Result.Failed; } finally { transaction.Commit(); Trace.Flush(); listener.Close(); Trace.Close(); Trace.Listeners.Remove(listener); } return retRes; } #endregion /// /// Connect the system with a trunk line of ducts on X axis /// /// the upper connectors of the vertical ducts that derived from the terminals and the base equipment /// the y value of the trunk line /// /// true if the system can be connected /// false if the system cannot be connected /// private bool ConnectSystemOnXAxis(List baseConnectors, double baseY) { //Check the count of the base connectors if (null == baseConnectors || 3 != baseConnectors.Count) { return false; } for (int i = 0; i < baseConnectors.Count; ++i) { //Check the distance of the connector from the trunk if (baseConnectors[i].Origin.Y != baseY && Math.Abs(baseConnectors[i].Origin.Y - baseY) < min1Duct2FittingsLength) { return false; } //Check the distance of the connectors on X axis for (int j = i + 1; j < baseConnectors.Count; ++j) { if (baseConnectors[j].Origin.X != baseConnectors[i].Origin.X && baseConnectors[j].Origin.X - baseConnectors[i].Origin.X < min2FittingsLength) { return false; } } } try { double baseZ = baseConnectors[0].Origin.Z + min1FittingLength; //Create the ducts and elbow fittings to connect the vertical ducts and the trunk ducts List connectors = new List(); if (baseConnectors[0].Origin.X == baseConnectors[1].Origin.X) { //All 3 connectors are with the same X value if (baseConnectors[1].Origin.X == baseConnectors[2].Origin.X) { return false; } else { //The 1st and 2nd base connectors are on the same side of the trunk if (Math.Sign(baseConnectors[0].Origin.Y - baseY) * Math.Sign(baseConnectors[1].Origin.Y - baseY) == 1) { return false; } //Create the trunk connectors = CreateDuct(new GXYZ(baseConnectors[0].Origin.X + min1FittingLength, baseY, baseZ), new GXYZ(baseConnectors[2].Origin.X - min1FittingLength, baseY, baseZ)); //Create a tee fitting connecting the 1st and 2nd base connectors to the trunk ConnectWithTeeFittingOnXAxis(baseConnectors[0], baseConnectors[1], connectors[0], true); //Create an elbow fitting connection the 3rd base connector to the trunk ConnectWithElbowFittingOnXAxis(baseConnectors[2], connectors[1]); } } else { //Create the segment of duct on the trunk to be connected to the 1st base connector connectors = CreateDuct(new GXYZ(baseConnectors[0].Origin.X + min1FittingLength, baseY, baseZ), new GXYZ(baseConnectors[1].Origin.X - min1FittingLength, baseY, baseZ)); //Create an elbow fitting connection the 1st base connector with the trunk ConnectWithElbowFittingOnXAxis(baseConnectors[0], connectors[0]); if (baseConnectors[1].Origin.X == baseConnectors[2].Origin.X) { //The 2nd and 3rd connectors are on the same side of the trunk if (Math.Sign(baseConnectors[1].Origin.Y - baseY) * Math.Sign(baseConnectors[2].Origin.Y - baseY) == 1) { return false; } //Create a tee fitting connecting the 2nd and 3rd base connectors to the trunk ConnectWithTeeFittingOnXAxis(baseConnectors[1], baseConnectors[2], connectors[1], true); } else { connectors.AddRange(CreateDuct(new GXYZ(baseConnectors[1].Origin.X + min1FittingLength, baseY, baseZ), new GXYZ(baseConnectors[2].Origin.X - min1FittingLength, baseY, baseZ))); //Create a tee fitting connecting the 2nd base connector to the trunk ConnectWithTeeFittingOnXAxis(baseConnectors[1], connectors[1], connectors[2], false); //Create an elbow fitting connection the 3rd base connector to the trunk ConnectWithElbowFittingOnXAxis(baseConnectors[2], connectors[3]); } } return true; } catch { return false; } } /// /// Connect a base connector to a connector on the trunk with an elbow fitting /// /// the upper connector of the vertical duct that derived from a terminal or the base equipment /// the connector of a duct on the trunk private void ConnectWithElbowFittingOnXAxis(Connector baseConn, Connector conn) { double baseY = conn.Origin.Y; double baseZ = conn.Origin.Z; List connectors = new List(); //If the distance of the two connectors on the Y axis is greater than 2, create a duct on the Y axis and then connect it to the 2 connectors with elbow fittings if (Math.Abs(baseConn.Origin.Y - baseY) > min1Duct2FittingsLength) { connectors.AddRange(CreateDuct(new GXYZ(baseConn.Origin.X, baseConn.Origin.Y - Math.Sign(baseConn.Origin.Y - baseY), baseZ), new GXYZ(baseConn.Origin.X, baseY + Math.Sign(baseConn.Origin.Y - baseY), baseZ))); connectors[0].ConnectTo(baseConn); m_document.Create.NewElbowFitting(connectors[0], baseConn); connectors[1].ConnectTo(conn); m_document.Create.NewElbowFitting(connectors[1], conn); } //If the distance of the two connectors on the Y axis is less than 2, connect them with an elbow fitting else { baseConn.ConnectTo(conn); m_document.Create.NewElbowFitting(baseConn, conn); } } /// /// Connect 3 connectors on the trunk with a tee fitting /// /// the first connector /// the second connector /// the third connector /// a flag to indicate whether there are 2 base connectors or 1 base connector private void ConnectWithTeeFittingOnXAxis(Connector conn1, Connector conn2, Connector conn3, bool flag) { double baseY = conn3.Origin.Y; double baseZ = conn3.Origin.Z; List points = new List(); List ducts = new List(); List connectors = new List(); //Connect two base connectors to a connector on the trunk if (true == flag) { Connector baseConn1 = conn1; Connector baseConn2 = conn2; Connector conn = conn3; connectors.AddRange(CreateDuct(new GXYZ(baseConn1.Origin.X, baseConn1.Origin.Y - Math.Sign(baseConn1.Origin.Y - baseY), baseZ), new GXYZ(baseConn1.Origin.X, baseY + Math.Sign(baseConn1.Origin.Y - baseY), baseZ))); connectors.AddRange(CreateDuct(new GXYZ(baseConn2.Origin.X, baseConn2.Origin.Y - Math.Sign(baseConn2.Origin.Y - baseY), baseZ), new GXYZ(baseConn2.Origin.X, baseY + Math.Sign(baseConn2.Origin.Y - baseY), baseZ))); connectors[0].ConnectTo(baseConn1); connectors[2].ConnectTo(baseConn2); m_document.Create.NewElbowFitting(connectors[0], baseConn1); m_document.Create.NewElbowFitting(connectors[2], baseConn2); connectors[1].ConnectTo(connectors[3]); connectors[1].ConnectTo(conn); connectors[3].ConnectTo(conn); m_document.Create.NewTeeFitting(connectors[1], connectors[3], conn); } //Connect a base connector to two connectors on the trunk else { Connector baseConn = conn1; if (Math.Abs(baseConn.Origin.Y - baseY) > min1Duct2FittingsLength) { connectors.AddRange(CreateDuct(new GXYZ(baseConn.Origin.X, baseConn.Origin.Y - Math.Sign(baseConn.Origin.Y - baseY), baseZ), new GXYZ(baseConn.Origin.X, baseY + Math.Sign(baseConn.Origin.Y - baseY), baseZ))); baseConn.ConnectTo(connectors[0]); m_document.Create.NewElbowFitting(connectors[0], baseConn); connectors[1].ConnectTo(conn2); connectors[1].ConnectTo(conn3); conn2.ConnectTo(conn3); m_document.Create.NewTeeFitting(conn2, conn3, connectors[1]); } else { baseConn.ConnectTo(conn2); baseConn.ConnectTo(conn3); conn2.ConnectTo(conn3); m_document.Create.NewTeeFitting(conn2, conn3, baseConn); } } } /// /// Sort the base connectors by their x values /// /// the connectors to be sorted private void SortConnectorsByX(List connectors) { for (int i = 0; i < connectors.Count; ++i) { double min = connectors[i].Origin.X; int minIndex = i; for (int j = i; j < connectors.Count; ++j) { if (connectors[j].Origin.X < min) { min = connectors[j].Origin.X; minIndex = j; } } Connector t = connectors[i]; connectors[i] = connectors[minIndex]; connectors[minIndex] = t; } } /// /// Connect the system with a trunk line of ducts on Y axis /// /// the upper connectors of the vertical ducts that derived from the terminals and the base equipment /// the x value of the trunk line /// /// true if the system can be connected /// false if the system cannot be connected /// private bool ConnectSystemOnYAxis(List baseConnectors, double baseX) { //Check the count of the base connectors if (null == baseConnectors || 3 != baseConnectors.Count) { return false; } for (int i = 0; i < baseConnectors.Count; ++i) { //Check the distance of the connector from the trunk if (baseConnectors[i].Origin.X != baseX && Math.Abs(baseConnectors[i].Origin.X - baseX) < min1Duct2FittingsLength) { return false; } //Check the distance of the connectors on Y axis for (int j = i + 1; j < baseConnectors.Count; ++j) { if (baseConnectors[j].Origin.Y != baseConnectors[i].Origin.Y && baseConnectors[j].Origin.Y - baseConnectors[i].Origin.Y < min2FittingsLength) { return false; } } } try { double baseZ = baseConnectors[0].Origin.Z + min1FittingLength; //Create the ducts and elbow fittings to connect the vertical ducts and the trunk ducts List connectors = new List(); if (baseConnectors[0].Origin.Y == baseConnectors[1].Origin.Y) { //All 3 connectors are with the same Y value if (baseConnectors[1].Origin.Y == baseConnectors[2].Origin.Y) { return false; } else { //The 1st and 2nd base connectors are on the same side of the trunk if (Math.Sign(baseConnectors[0].Origin.X - baseX) * Math.Sign(baseConnectors[1].Origin.X - baseX) == 1) { return false; } //Create the trunk connectors = CreateDuct(new GXYZ(baseX, baseConnectors[0].Origin.Y + min1FittingLength, baseZ), new GXYZ(baseX, baseConnectors[2].Origin.Y - min1FittingLength, baseZ)); //Create a tee fitting connecting the 1st and 2nd base connectors to the trunk ConnectWithTeeFittingOnYAxis(baseConnectors[0], baseConnectors[1], connectors[0], true); //Create an elbow fitting connection the 3rd base connector to the trunk ConnectWithElbowFittingOnYAxis(baseConnectors[2], connectors[1]); } } else { //Create the segment of duct on the trunk to be connected to the 1st base connector connectors = CreateDuct(new GXYZ(baseX, baseConnectors[0].Origin.Y + min1FittingLength, baseZ), new GXYZ(baseX, baseConnectors[1].Origin.Y - min1FittingLength, baseZ)); //Create an elbow fitting connection the 1st base connector with the trunk ConnectWithElbowFittingOnYAxis(baseConnectors[0], connectors[0]); if (baseConnectors[1].Origin.Y == baseConnectors[2].Origin.Y) { //The 2nd and 3rd connectors are on the same side of the trunk if (Math.Sign(baseConnectors[1].Origin.X - baseX) * Math.Sign(baseConnectors[2].Origin.X - baseX) == 1) { return false; } //Create a tee fitting connecting the 2nd and 3rd base connectors to the trunk ConnectWithTeeFittingOnYAxis(baseConnectors[1], baseConnectors[2], connectors[1], true); } else { connectors.AddRange(CreateDuct(new GXYZ(baseX, baseConnectors[1].Origin.Y + min1FittingLength, baseZ), new GXYZ(baseX, baseConnectors[2].Origin.Y - min1FittingLength, baseZ))); //Create a tee fitting connecting the 2nd base connector to the trunk ConnectWithTeeFittingOnYAxis(baseConnectors[1], connectors[1], connectors[2], false); //Create an elbow fitting connection the 3rd base connector to the trunk ConnectWithElbowFittingOnYAxis(baseConnectors[2], connectors[3]); } } return true; } catch { return false; } } /// /// Connect a base connector to a connector on the trunk with an elbow fitting /// /// the upper connector of the vertical duct that derived from a terminal or the base equipment /// the connector of a duct on the trunk private void ConnectWithElbowFittingOnYAxis(Connector baseConn, Connector conn) { double baseX = conn.Origin.X; double baseZ = conn.Origin.Z; List connectors = new List(); //If the distance of the two connectors on the X axis is greater than 2, create a duct on the X axis and then connect it to the 2 connectors with elbow fittings if (Math.Abs(baseConn.Origin.X - baseX) > min1Duct2FittingsLength) { connectors.AddRange(CreateDuct(new GXYZ(baseConn.Origin.X - Math.Sign(baseConn.Origin.X - baseX), baseConn.Origin.Y, baseZ), new GXYZ(baseX + Math.Sign(baseConn.Origin.X - baseX), baseConn.Origin.Y, baseZ))); connectors[0].ConnectTo(baseConn); m_document.Create.NewElbowFitting(connectors[0], baseConn); connectors[1].ConnectTo(conn); m_document.Create.NewElbowFitting(connectors[1], conn); } //If the distance of the two connectors on the X axis is less than 2, connect them with an elbow fitting else { baseConn.ConnectTo(conn); m_document.Create.NewElbowFitting(baseConn, conn); } } /// /// Connect 3 connectors on the trunk with a tee fitting /// /// the first connector /// the second connector /// the third connector /// a flag to indicate whether there are 2 base connectors or 1 base connector private void ConnectWithTeeFittingOnYAxis(Connector conn1, Connector conn2, Connector conn3, bool flag) { double baseX = conn3.Origin.X; double baseZ = conn3.Origin.Z; List points = new List(); List ducts = new List(); List connectors = new List(); //Connect two base connectors to a connector on the trunk if (true == flag) { Connector baseConn1 = conn1; Connector baseConn2 = conn2; Connector conn = conn3; connectors.AddRange(CreateDuct(new GXYZ(baseConn1.Origin.X - Math.Sign(baseConn1.Origin.X - baseX), baseConn1.Origin.Y, baseZ), new GXYZ(baseX + Math.Sign(baseConn1.Origin.X - baseX), baseConn1.Origin.Y, baseZ))); connectors.AddRange(CreateDuct(new GXYZ(baseConn2.Origin.X - Math.Sign(baseConn2.Origin.X - baseX), baseConn2.Origin.Y, baseZ), new GXYZ(baseX + Math.Sign(baseConn2.Origin.X - baseX), baseConn2.Origin.Y, baseZ))); connectors[0].ConnectTo(baseConn1); connectors[2].ConnectTo(baseConn2); m_document.Create.NewElbowFitting(connectors[0], baseConn1); m_document.Create.NewElbowFitting(connectors[2], baseConn2); connectors[1].ConnectTo(connectors[3]); connectors[1].ConnectTo(conn); connectors[3].ConnectTo(conn); m_document.Create.NewTeeFitting(connectors[1], connectors[3], conn); } //Connect a base connector to two connectors on the trunk else { Connector baseConn = conn1; if (Math.Abs(baseConn.Origin.X - baseX) > min1Duct2FittingsLength) { connectors.AddRange(CreateDuct(new GXYZ(baseConn.Origin.X - Math.Sign(baseConn.Origin.X - baseX), baseConn.Origin.Y, baseZ), new GXYZ(baseX + Math.Sign(baseConn.Origin.X - baseX), baseConn.Origin.Y, baseZ))); baseConn.ConnectTo(connectors[0]); m_document.Create.NewElbowFitting(connectors[0], baseConn); connectors[1].ConnectTo(conn2); connectors[1].ConnectTo(conn3); conn2.ConnectTo(conn3); m_document.Create.NewTeeFitting(conn2, conn3, connectors[1]); } else { baseConn.ConnectTo(conn2); baseConn.ConnectTo(conn3); conn2.ConnectTo(conn3); m_document.Create.NewTeeFitting(conn2, conn3, baseConn); } } } /// /// Sort the base connectors by their y values /// /// the connectors to be sorted private void SortConnectorsByY(List connectors) { for (int i = 0; i < connectors.Count; ++i) { double min = connectors[i].Origin.Y; int minIndex = i; for (int j = i; j < connectors.Count; ++j) { if (connectors[j].Origin.Y < min) { min = connectors[j].Origin.Y; minIndex = j; } } Connector t = connectors[i]; connectors[i] = connectors[minIndex]; connectors[minIndex] = t; } } /// /// Create a duct with two points /// /// the first point /// the second point /// private List CreateDuct(GXYZ point1, GXYZ point2) { List connectors = new List(); Duct duct = Duct.Create(m_document, systemTypeId, ductTypeId, lvl.Id, point1, point2); connectors.Add(ConnectorInfo.GetConnector(duct.Id, point1)); connectors.Add(ConnectorInfo.GetConnector(duct.Id, point2)); return connectors; } /// /// Create a mechanical system /// /// the base connector of the mechanical system /// the connectors of the mechanical system /// the system type of the mechanical system /// the created mechanical system private MechanicalSystem CreateMechanicalSystem(ConnectorInfo baseConnector, ConnectorInfo[] connectors, DuctSystemType systemType) { ConnectorSet cset = null; if (connectors != null) { cset = new ConnectorSet(); foreach (ConnectorInfo ci in connectors) { cset.Insert(ci.Connector); } } MechanicalSystem mechanicalSystem = m_document.Create.NewMechanicalSystem(baseConnector == null ? null : baseConnector.Connector, cset, systemType); return mechanicalSystem; } /// /// information of a connector /// public class ConnectorInfo { /// /// The owner's element ID /// ElementId m_ownerId; /// /// The origin of the connector /// GXYZ m_origin; /// /// The Connector object /// Connector m_connector; /// /// The connector this object represents /// public Connector Connector { get { return m_connector; } set { m_connector = value; } } /// /// The owner ID of the connector /// public ElementId OwnerId { get { return m_ownerId; } set { m_ownerId = value; } } /// /// The origin of the connector /// public GXYZ Origin { get { return m_origin; } set { m_origin = value; } } /// /// The constructor that finds the connector with the owner ID and origin /// /// the ownerID of the connector /// the origin of the connector public ConnectorInfo(ElementId ownerId, GXYZ origin) { m_ownerId = ownerId; m_origin = origin; m_connector = ConnectorInfo.GetConnector(m_ownerId, origin); } /// /// The constructor that finds the connector with the owner ID and the values of the origin /// /// the ownerID of the connector /// the X value of the connector /// the Y value of the connector /// the Z value of the connector public ConnectorInfo(ElementId ownerId, double x, double y, double z) : this(ownerId, new GXYZ(x, y, z)) { } /// /// Get the connector of the owner at the specific origin /// /// the owner ID of the connector /// the origin of the connector /// if found, return the connector, or else return null public static Connector GetConnector(ElementId ownerId, GXYZ connectorOrigin) { ConnectorSet connectors = GetConnectors(ownerId); foreach (Connector conn in connectors) { if (conn.ConnectorType == ConnectorType.Logical) continue; if (conn.Origin.IsAlmostEqualTo(connectorOrigin)) return conn; } return null; } /// /// Get all the connectors of an element with a specific ID /// /// the owner ID of the connector /// the connector set which includes all the connectors found public static ConnectorSet GetConnectors(ElementId ownerId) { Element element = m_document.GetElement(ownerId); return GetConnectors(element); } /// /// Get all the connectors of a specific element /// /// the owner of the connector /// if found, return all the connectors found, or else return null public static ConnectorSet GetConnectors(Autodesk.Revit.DB.Element element) { if (element == null) return null; FamilyInstance fi = element as FamilyInstance; if (fi != null && fi.MEPModel != null) { return fi.MEPModel.ConnectorManager.Connectors; } MEPSystem system = element as MEPSystem; if (system != null) { return system.ConnectorManager.Connectors; } MEPCurve duct = element as MEPCurve; if (duct != null) { return duct.ConnectorManager.Connectors; } return null; } /// /// Find the two connectors of the specific ConnectorManager at the two locations /// /// The ConnectorManager of the connectors to be found /// the location of the first connector /// the location of the second connector /// The two connectors found public static Connector[] FindConnectors(ConnectorManager connMgr, GXYZ pnt1, GXYZ pnt2) { Connector[] result = new Connector[2]; ConnectorSet conns = connMgr.Connectors; foreach (Connector conn in conns) { if (conn.Origin.IsAlmostEqualTo(pnt1)) { result[0] = conn; } else if (conn.Origin.IsAlmostEqualTo(pnt2)) { result[1] = conn; } } return result; } }; } public class LogUtility { /// /// Invalid string. /// public const string InvalidString = "[!]"; /// /// Write the information of an element to the log file /// /// the element whose information is to be written public static void WriteElement(Element element) { WriteElement(element, true); } /// /// Write the information of an element to the log file /// /// the element whose information is to be written /// whether the id will be outputted public static void WriteElement(Element element, bool writeId) { if (element == null) { Trace.WriteLine("null"); return; } ElementId elementId = element.Id; ElementId familyId = element.get_Parameter(BuiltInParameter.ELEM_FAMILY_PARAM).AsElementId(); string familyName = LogUtility.InvalidString; Element objectType = GetElement(element.Document, familyId); string elementName = LogUtility.InvalidString; try { elementName = element.Name; } catch { } if (objectType != null) { Parameter familyNameParameter = objectType.get_Parameter(BuiltInParameter.ALL_MODEL_FAMILY_NAME); if (familyNameParameter != null) familyName = familyNameParameter.AsString(); } BuiltInCategory category = element.Category.BuiltInCategory; Trace.WriteLine("Type: " + element.GetType().FullName); Trace.WriteLine("Name: " + familyName + ":" + elementName); if (writeId) Trace.WriteLine("Id: " + elementId.ToString()); Trace.WriteLine("Category: " + category); Trace.WriteLine("FamilyId: " + familyId.ToString()); } /// /// Write the information of a mechanical system to the log file /// /// the mechanical system whose information is to be written public static void WriteMechanicalSystem(MechanicalSystem system) { string flow = InvalidString; try { flow = system.GetFlow().ToString(); } catch (Exception) { } Trace.WriteLine("Flow: " + flow); Trace.WriteLine("IsWellConnected: " + system.IsWellConnected); Trace.WriteLine("SystemType: " + system.SystemType); Trace.WriteLine("+DuctNetwork"); Trace.Indent(); foreach (Element element in system.DuctNetwork) { LogUtility.WriteElement(element, false); Trace.WriteLine(""); } Trace.Unindent(); WriteMEPSystem(system); } /// /// Get element by its id and cast it to the specified type /// /// the owner document of the element /// the id of the element /// the element of the specified type public static T GetElement(Document document, ElementId eid) where T : Autodesk.Revit.DB.Element { return document.GetElement(eid) as T; } /// /// Write the information of a MEPSystem to the log file. /// /// the MEP system whose information is to be written public static void WriteMEPSystem(MEPSystem system) { WriteElement(system.BaseEquipment); Trace.Unindent(); Trace.WriteLine("+BaseEquipmentConnector"); Trace.Indent(); WriteConnector(system.BaseEquipmentConnector); Trace.Unindent(); Trace.WriteLine("+Elements"); Trace.Indent(); foreach (Element element in system.Elements) { WriteElement(element); Trace.WriteLine(""); } Trace.Unindent(); Trace.WriteLine("+ConnectorManager"); Trace.Indent(); WriteConnectorManager(system.ConnectorManager); Trace.Unindent(); } /// /// Write the information of a connector to the log file /// /// the connector whose information is to be written public static void WriteConnector(Connector connector) { if (connector == null) { Trace.WriteLine("null"); return; } object connType = InvalidString; object connDirection = InvalidString; object connShape = InvalidString; try { connShape = connector.Shape; } catch { } object connSize = InvalidString; try { connSize = GetShapeInfo(connector); } catch { } object connLocation = GetLocation(connector); object connAType = connector.ConnectorType; object connIsConnected = InvalidString; switch (connector.Domain) { case Domain.DomainElectrical: connType = connector.ElectricalSystemType; break; case Domain.DomainHvac: connType = connector.DuctSystemType; connDirection = connector.Direction; connIsConnected = connector.IsConnected; break; case Domain.DomainPiping: connType = connector.PipeSystemType; connDirection = connector.Direction; connIsConnected = connector.IsConnected; break; case Domain.DomainUndefined: default: connType = Domain.DomainUndefined; break; } Trace.WriteLine("Type: " + connAType); Trace.WriteLine("SystemType: " + connType); Trace.WriteLine("Direction: " + connDirection); Trace.WriteLine("Shape: " + connShape); Trace.WriteLine("Size: " + connSize); Trace.WriteLine("Location: " + connLocation); Trace.WriteLine("IsConnected: " + connIsConnected); } /// /// Write the information of a ConnectorManager to the log file /// /// the ConnectorManager whose information is to be written public static void WriteConnectorManager(ConnectorManager connectorManager) { Trace.WriteLine("+Connectors"); Trace.Indent(); WriteConnectorSet2(connectorManager.Connectors); Trace.Unindent(); Trace.WriteLine("+UnusedConnectors"); Trace.Indent(); WriteConnectorSet2(connectorManager.UnusedConnectors); Trace.Unindent(); } /// /// Get the information string of a connector /// /// the connector to be read /// the information string of the connector public static string GetConnectorId(Connector connector) { if (connector == null) { return "null"; } ElementId ownerId = connector.Owner.Id; string systemId = InvalidString; try { systemId = connector.MEPSystem.Id.ToString(); } catch { } object connType = InvalidString; object connDirection = InvalidString; object connShape = InvalidString; try { connShape = connector.Shape; } catch { } object connSize = InvalidString; try { connSize = GetShapeInfo(connector); } catch { } object connLocation = GetLocation(connector); object connAType = connector.ConnectorType; object connIsConnected = InvalidString; switch (connector.Domain) { case Domain.DomainElectrical: connType = connector.ElectricalSystemType; break; case Domain.DomainHvac: connType = connector.DuctSystemType; connDirection = connector.Direction; connIsConnected = connector.IsConnected; break; case Domain.DomainPiping: connType = connector.PipeSystemType; connDirection = connector.Direction; connIsConnected = connector.IsConnected; break; case Domain.DomainUndefined: default: connType = Domain.DomainUndefined; break; } return string.Format("[{0}]\t[{1}]\t[{2}]\t[{3}]\t[{4}]\t[{5}]\t[{6}]\t[{7}]\t[{8}]\t", ownerId.ToString(), connType, connDirection, connShape, connSize, connLocation, connAType, connIsConnected, systemId); } /// /// Get the shape information string of a connector /// /// the element to be read /// the shape information string of the connector private static string GetShapeInfo(Connector conn) { switch (conn.Shape) { case ConnectorProfileType.Invalid: break; case ConnectorProfileType.Oval: break; case ConnectorProfileType.Rectangular: return string.Format("{0}\" x {1}\"", conn.Width, conn.Height); case ConnectorProfileType.Round: return string.Format("{0}\"", conn.Radius); default: break; } return InvalidString; } /// /// Get the location string of a connector /// /// the connector to be read /// the location information string of the connector private static object GetLocation(Connector conn) { if (conn.ConnectorType == ConnectorType.Logical) { return InvalidString; } Autodesk.Revit.DB.XYZ origin = conn.Origin; return string.Format("{0},{1},{2}", origin.X, origin.Y, origin.Z); } /// /// Write the information of a ConnectorSet to the log file. /// /// the ConnectorSet whose information is to be written private static void WriteConnectorSet2(ConnectorSet connectorSet) { SortedDictionary> connectors = new SortedDictionary>(); foreach (Connector conn in connectorSet) { string connId = GetConnectorId(conn); if (conn.ConnectorType == ConnectorType.Logical) { foreach (Connector logLinkConn in conn.AllRefs) { connId += GetConnectorId(logLinkConn); } } if (!connectors.ContainsKey(connId)) { connectors.Add(connId, new List()); } connectors[connId].Add(conn); } foreach (string key in connectors.Keys) { foreach (Connector conn in connectors[key]) { WriteConnector(conn); Trace.WriteLine("+AllRefs"); Trace.Indent(); WriteConnectorSet(conn.AllRefs); Trace.Unindent(); Trace.WriteLine(""); } } } /// /// Write the information of a ConnectorSet to the log file. /// /// the mechanical system whose information is to be written private static void WriteConnectorSet(ConnectorSet connectorSet) { SortedDictionary> connectors = new SortedDictionary>(); foreach (Connector conn in connectorSet) { string connId = GetConnectorId(conn); if (conn.ConnectorType == ConnectorType.Logical) { foreach (Connector logLinkConn in conn.AllRefs) { connId += GetConnectorId(logLinkConn); } } if (!connectors.ContainsKey(connId)) { connectors.Add(connId, new List()); } connectors[connId].Add(conn); } foreach (string key in connectors.Keys) { foreach (Connector conn in connectors[key]) { WriteConnector(conn); Trace.WriteLine(""); } } } } }