using Autodesk.Revit.DB; using Autodesk.Revit.DB.Mechanical; using Autodesk.Revit.DB.Plumbing; using Autodesk.Revit.UI; using KMBIM.Utils; using KMBIM.Revit.Tools.Utils; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows.Forms; using KDCS.Utils; namespace KMBIM.Revit.Tools.Cmd.PipeConnection { public class ResolverForPipeConnect { public enum EnumResolverMode { Undef, RefBaseModelLine, Prev, Next, Run, Close } private UIApplication m_rvtUIApp; private UIDocument m_rvtUIDoc; private Document m_rvtDoc; private Autodesk.Revit.ApplicationServices.Application m_rvtApp; public Autodesk.Revit.UI.UIDocument uidoc { get; set; } private int m_inx; private bool m_bFlexible; private bool m_bRefFloorBaseLine; Dictionary> m_Routes = new Dictionary>(); List lstCurve = new List(); EnumResolverMode m_ResolverMode; ElementId systemTypeId = null; Element m_baseElementOrigin = null; Element m_baseElement = null; Element m_targetElement = null; Connector m_baseConnectorOrigin = null; Connector m_baseConnector = null; Connector m_targetConnector = null; Pipe basePipe = null; Pipe targetPipe = null; PipeType pipeType = null; FlexPipeType flexPipeType = null; private double m_pipeDiameter; //private int m_LineStyle = (int)BuiltInCategory.OST_HiddenLines; private int m_LineStyle = (int)BuiltInCategory.OST_Lines; public ResolverForPipeConnect(UIApplication uiapp) { m_rvtUIApp = uiapp; m_rvtApp = uiapp.Application; m_rvtUIDoc = uiapp.ActiveUIDocument; m_rvtDoc = m_rvtUIDoc.Document; m_inx = 0; m_bFlexible = false; m_bRefFloorBaseLine = false; } public void SetRefFloorBaseLine(bool bRefFloorBaseLine) { m_bRefFloorBaseLine = bRefFloorBaseLine; } public void SetResolverMode(EnumResolverMode mode) { m_ResolverMode = mode; } public void SetRoute(ref Dictionary> route) { this.m_Routes = route; m_inx = 0; } public void SetPipeDiameter(double dia) { m_pipeDiameter = dia; } public void SetBaseElementOrigin(Element elm, Connector con) { this.m_baseElementOrigin = elm; this.m_baseConnectorOrigin = con; Pipe pipe1 = elm as Pipe; if (pipe1 != null) { this.pipeType = pipe1.PipeType; if (systemTypeId == null) { systemTypeId = pipe1.MEPSystem.GetTypeId(); } }else if(con != null){ if (con.MEPSystem != null) { systemTypeId = con.MEPSystem.GetTypeId(); } } } public void SetElement(Element e1, Element e2) { this.m_baseElement = e1; this.m_targetElement = e2; if (pipeType == null) { Pipe pipe1 = e1 as Pipe; if (pipe1 != null) { this.pipeType = pipe1.PipeType; } else { Pipe pipe2 = e2 as Pipe; if (pipe2 != null) { this.pipeType = pipe2.PipeType; } } } } public void SetConnector(Connector c1, Connector c2) { this.m_baseConnector = c1; this.m_targetConnector = c2; } public void GetRoute(ref Dictionary> route) { route = this.m_Routes; } public int GetIndex() { return this.m_Routes.Keys.Count > 0 ? m_inx % this.m_Routes.Keys.Count : m_inx; } public void MoveNext() { if (m_inx >= (m_Routes.Keys.Count - 1)) m_inx = 0; else m_inx += 1; } public void MovePrevious() { if (m_inx > 0) { m_inx -= 1; } } public void SetSystemType(ElementId systemtypeid) { this.systemTypeId = systemtypeid; } public ElementId GetSystemType() { return this.systemTypeId; } private void DrawRoute() { if (this.m_Routes == null) return; if (this.m_Routes.Count < 1) return; var levelId = m_baseElement.LevelId; Pipe pipeBase = m_baseElement as Pipe; if (pipeBase != null) { levelId = pipeBase.ReferenceLevel.Id; } if (this.pipeType == null) { var pipeTypes = new FilteredElementCollector(m_rvtDoc).OfClass(typeof(PipeType)).ToElements(); if (pipeTypes.Count > 0) { pipeType = pipeTypes[pipeTypes.Count - 1] as PipeType; } } var lst = this.m_Routes[(m_inx + 1).ToString()]; if (lst == null) return; using (Transaction tr = new Transaction(m_rvtDoc, "Create3DModelLine")) { tr.Start(); // Line is slightly off axis and may cause inaccuracies. // (선이 축을 약간 벗어났습니다.부정확새질 수 있습니다.) // => This is not an error, it is a warning. You can pass warnings but can't pass errors. FailureHandlingOptions failureHandlingOptions = tr.GetFailureHandlingOptions(); failureHandlingOptions.SetFailuresPreprocessor(new KdcsFailureHandler()); tr.SetFailureHandlingOptions(failureHandlingOptions); for (int i = 0; i < lst.Count - 1; i++) { XYZ p1 = lst[i]; XYZ p2 = lst[i + 1]; var crv = Pipe.CreatePlaceholder(m_rvtDoc, systemTypeId, pipeType.Id, levelId, p1, p2); if (crv != null) lstCurve.Add(crv); } if (lstCurve.Count > 2) { for (int i = 0; i < lstCurve.Count - 1; i++) { Pipe pipe1 = lstCurve.ElementAt(i); Pipe pipe2 = lstCurve.ElementAt(i+1); Connector sc1, ec1, sc2, ec2; sc1=ec1=sc2=ec2=null; Util.GetStartEndConnector(pipe1, ref sc1, ref ec1); Util.GetStartEndConnector(pipe2, ref sc2, ref ec2); ec1.ConnectTo(sc2); } } tr.Commit(); } } public void DeleteCurves() { if (lstCurve.Count < 1) return; using (Transaction tr = new Transaction(m_rvtDoc, "Delete Curves")) { tr.Start(); foreach (var crv in lstCurve) { m_rvtDoc.Delete(crv.Id); } lstCurve.Clear(); tr.Commit(); } } public void SetFlexible(bool bFlexible) { m_bFlexible = bFlexible; } public void Run() { try { if (this.m_ResolverMode == EnumResolverMode.Prev) { DeleteCurves(); m_inx = GetIndex(); DrawRoute(); } else if (this.m_ResolverMode == EnumResolverMode.Next) { DeleteCurves(); m_inx = GetIndex(); DrawRoute(); } else if(this.m_ResolverMode == EnumResolverMode.Run) { basePipe = m_baseElement as Pipe; targetPipe = m_targetElement as Pipe; if (this.pipeType == null) { var pipeTypes = new FilteredElementCollector(m_rvtDoc).OfClass(typeof(PipeType)).ToElements(); if (pipeTypes.Count > 0) { pipeType = pipeTypes[pipeTypes.Count - 1] as PipeType; } } if (this.flexPipeType == null) { var pipeTypes = new FilteredElementCollector(m_rvtDoc).OfClass(typeof(FlexPipeType)).ToElements(); if (pipeTypes.Count > 0) { flexPipeType = pipeTypes[pipeTypes.Count - 1] as FlexPipeType; } } if (pipeType != null) { using (Transaction tr = new Transaction(m_rvtDoc, "CreatePipes")) { tr.Start(); CreatePipe(m_rvtDoc, lstCurve); tr.Commit(); } } DeleteCurves(); App.thisApp.m_wfPipeConnector.Close(); } else if (this.m_ResolverMode == EnumResolverMode.Close) { DeleteCurves(); } m_rvtUIDoc.RefreshActiveView(); } catch (Exception ex) { MessageBox.Show(ex.Message); } } // Clamps value between 0 and 1 and returns value public static float Clamp01(float value) { if (value < 0F) return 0F; else if (value > 1F) return 1F; else return value; } public static float InverseLerp(float a, float b, float value) { if (a != b) return Clamp01((value - a) / (b - a)); else return 0.0f; } /// /// 주어진 arrayToCurve 점들을 이용하여 커브점을 만들어 넘겨준다. /// /// 정점 배열 /// 보간 수 /// public static XYZ[] MakeSmoothCurve(XYZ[] arrayToCurve, float smoothness) { List points; List curvedPoints; int pointsLength = 0; int curvedLength = 0; if (smoothness < 1.0f) smoothness = 1.0f; pointsLength = arrayToCurve.Length; curvedLength = (pointsLength * Convert.ToInt16(Math.Round(smoothness))) - 1; curvedPoints = new List(curvedLength); float t = 0.0f; for (int pointInTimeOnCurve = 0; pointInTimeOnCurve < curvedLength + 1; pointInTimeOnCurve++) { t = InverseLerp(0, curvedLength, pointInTimeOnCurve); points = new List(arrayToCurve); for (int j = pointsLength - 1; j > 0; j--) { for (int i = 0; i < j; i++) { points[i] = (1 - t) * points[i] + t * points[i + 1]; } } curvedPoints.Add(points[0]); } return (curvedPoints.ToArray()); } /// /// 파이프 생성 /// /// /// /// public void CreatePipe(Document document, List curveList) { //var paramLevel = m_baseElement.get_Parameter(BuiltInParameter.RBS_START_LEVEL_PARAM); var levelId = m_baseElement.LevelId; //.IntegerValue != -1 ? m_baseElement.LevelId : m_targetElement.LevelId; Pipe pipeBase = m_baseElement as Pipe; if (pipeBase != null) { levelId = pipeBase.ReferenceLevel.Id; } if (pipeType == null) return; List lst = new List(); bool isFirst = true; foreach (var pip in curveList) { LocationCurve locCrv = pip.Location as LocationCurve; XYZ sp = locCrv.Curve.GetEndPoint(0); XYZ ep = locCrv.Curve.GetEndPoint(1); if (isFirst) { if (m_baseConnector.Origin.DistanceTo(sp) > m_baseConnector.Origin.DistanceTo(ep)) { Util.Swap(ref sp, ref ep); } lst.Add(sp); lst.Add(ep); } else { XYZ pLast = lst.Last(); if(pLast.DistanceTo(sp) > pLast.DistanceTo(ep)) lst.Add(sp); else lst.Add(ep); } isFirst = false; } Pipe newPipe = null; XYZ start = null; XYZ end = null; XYZ splitpoint = null; List lstTmp = new List(); List lstCrv = new List(); if (m_bFlexible && flexPipeType!=null) { //var lst = this.m_Routes[(m_inx + 1).ToString()]; if (lst.Count > 2) { lstTmp.Add(lst.First()); int dur = lst.Count - 1; for (int i = 1; i < dur; i++) { XYZ fp0 = lst.ElementAt(i-1); XYZ fp1 = lst.ElementAt(i); XYZ fp2 = lst.ElementAt(i + 1); XYZ fpInsm = fp1 + (fp0 - fp1).Normalize() * fp0.DistanceTo(fp1) * 0.5; XYZ fpIns0 = fp1 + (fp0 - fp1).Normalize() * m_pipeDiameter * 5; XYZ fpIns1 = fp1 + (fp0 - fp1).Normalize() * m_pipeDiameter * 4; XYZ fpIns2 = fp1 + (fp0 - fp1).Normalize() * m_pipeDiameter * 3.0; XYZ fpIns3 = fp1 + (fp2 - fp1).Normalize() * m_pipeDiameter * 3.0; XYZ fpIns4 = fp1 + (fp2 - fp1).Normalize() * m_pipeDiameter * 4; XYZ fpIns5 = fp1 + (fp2 - fp1).Normalize() * m_pipeDiameter * 5; lstCrv.Clear(); lstCrv.Add(fpIns0); lstCrv.Add(fpIns1); lstCrv.Add(fpIns2); lstCrv.Add(fpIns3); lstCrv.Add(fpIns4); lstCrv.Add(fpIns5); var inArr = lstCrv.ToArray(); var arrcrv = MakeSmoothCurve(inArr, 1.5f); lstTmp.Add(fpInsm); lstTmp.AddRange(arrcrv); } XYZ plp = lst.ElementAt(lst.Count - 2); XYZ pll = lst.Last(); lstTmp.Add(Util.Midpoint(plp, pll)); lstTmp.Add(lst.Last()); lst = lstTmp.ToList(); } FlexPipe newFlexPipe = FlexPipe.Create(m_rvtDoc, systemTypeId, flexPipeType.Id, levelId, lst.ToArray()); if (newFlexPipe != null) { LocationCurve locCrv = newFlexPipe.Location as LocationCurve; if (locCrv != null) { newFlexPipe.StartTangent = m_baseConnector.CoordinateSystem.BasisZ; newFlexPipe.EndTangent = m_targetConnector.CoordinateSystem.BasisZ; } newFlexPipe.get_Parameter(BuiltInParameter.RBS_PIPE_DIAMETER_PARAM).Set(m_pipeDiameter); Connector pipeConnectorFirst = FindConnector(newFlexPipe, m_baseConnector.CoordinateSystem.Origin); if (pipeConnectorFirst != null) { if (Util.IsEqual(m_baseConnector.Radius, pipeConnectorFirst.Radius)) { pipeConnectorFirst.ConnectTo(m_baseConnector); } else { try{ m_rvtDoc.Create.NewTransitionFitting(pipeConnectorFirst, m_baseConnector); } catch (Autodesk.Revit.Exceptions.InvalidOperationException ex) { MessageBox.Show("기준 커넥터 위치에 변환 피팅을 삽입할 수 없습니다."); } catch (Exception ex) { } } } Connector pipeConnectorLast = FindConnector(newFlexPipe, m_targetConnector.CoordinateSystem.Origin); if (Util.IsEqual(m_targetConnector.Radius, pipeConnectorLast.Radius)) { pipeConnectorLast.ConnectTo(m_targetConnector); } else { try { m_rvtDoc.Create.NewTransitionFitting(pipeConnectorLast, m_targetConnector); } catch (Autodesk.Revit.Exceptions.InvalidOperationException ex) { MessageBox.Show("대상 커넥터 위치에 변환 피팅을 삽입할 수 없습니다."); } catch (Exception ex) { } } m_rvtDoc.Regenerate(); } return; } // 경로선 List lines = new List(); foreach (Pipe pipe in curveList) { Line line = (pipe.Location as LocationCurve).Curve as Line; lines.Add(line); } //PlumbingUtils.ConvertPipePlaceholders(document, lines); if (basePipe != null) { Line crv2 = lines.First(); Line crv1 = (basePipe.Location as LocationCurve).Curve as Line; if (Util.IsParallel(crv1.Direction, crv2.Direction)) { XYZ ptBase = m_baseConnector.CoordinateSystem.Origin; if (crv2.GetEndPoint(0).DistanceTo(ptBase) > crv2.GetEndPoint(1).DistanceTo(ptBase)) Util.SetPipeEnd(basePipe, crv2.GetEndPoint(0)); else Util.SetPipeEnd(basePipe, crv2.GetEndPoint(1)); lines.RemoveAt(0); } } List pipes = new List(); foreach (Line line in lines) { start = line.GetEndPoint(0); end = line.GetEndPoint(1); newPipe = Pipe.Create(document, systemTypeId, pipeType.Id, levelId, start, end); pipes.Add(newPipe); newPipe.get_Parameter(BuiltInParameter.RBS_PIPE_DIAMETER_PARAM).Set(m_pipeDiameter); } // 파이프 연결 for (int i = 1; i < pipes.Count(); i++) { Bend(pipes[i - 1], pipes[i]); } document.Regenerate(); if (basePipe != null) { var pipeFirst = pipes.First(); Bend(basePipe, pipeFirst); } else { // 베이스 커넥터와 첫번째 파이프 연결 var pipeFirst = pipes.First(); Connector pipeConnectorFirst = FindConnector(pipeFirst, m_baseConnector.CoordinateSystem.Origin); if (pipeConnectorFirst != null) { if (Util.IsEqual(m_baseConnector.Radius, pipeConnectorFirst.Radius)) { pipeConnectorFirst.ConnectTo(m_baseConnector); } else m_rvtDoc.Create.NewTransitionFitting(pipeConnectorFirst, m_baseConnector); } } var pipeLast = pipes.Last(); if (targetPipe != null) { Bend(targetPipe, pipeLast); } else { Connector pipeConnectorLast = FindConnector(pipeLast, m_targetConnector.CoordinateSystem.Origin); if (Util.IsEqual(m_targetConnector.Radius, pipeConnectorLast.Radius)) { pipeConnectorLast.ConnectTo(m_targetConnector); } else m_rvtDoc.Create.NewTransitionFitting(pipeConnectorLast, m_targetConnector); } } private XYZ ParallelInters(Line crv1, Line crv2) { XYZ itr = null; double tol = 0.0328084; // ft = 1cm double min_len = double.MaxValue; if (crv1.GetEndPoint(0).DistanceTo(crv2.GetEndPoint(0)) < tol) { itr = crv1.GetEndPoint(0); } if (crv1.GetEndPoint(0).DistanceTo(crv2.GetEndPoint(1)) < tol) { itr = crv1.GetEndPoint(0); } else if (crv1.GetEndPoint(1).DistanceTo(crv2.GetEndPoint(0)) < tol) { itr = crv1.GetEndPoint(1); } else if (crv1.GetEndPoint(1).DistanceTo(crv2.GetEndPoint(1)) < tol) { itr = crv1.GetEndPoint(1); } return itr; } /// /// 주어진 두개으 파이프를 엘보로 연결한다. /// /// /// public void Bend(Pipe pipe1, Pipe pipe2) { Autodesk.Revit.DB.Parameter paramLevel = pipe1.get_Parameter(BuiltInParameter.RBS_START_LEVEL_PARAM); ElementId levelId = paramLevel.AsElementId(); Line crv1 = (pipe1.Location as LocationCurve).Curve as Line; Line crv2 = (pipe2.Location as LocationCurve).Curve as Line; XYZ itr = null; if(Util.IsParallel(crv1.Direction, crv2.Direction)){ itr = ParallelInters(crv1, crv2); } else { itr = Util.GetCurvesIntersectionPoint(crv1, crv2); } // 2개의 파이프가 서로 만나는 쪽의 커텍터 찾기 Connector connector1 = FindConnector(pipe1, itr); Connector connector2 = FindConnector(pipe2, itr); FamilyInstance fii = null; if (connector1 != null && connector2 != null) { double tol = 0.001; if (Util.IsZero(connector1.CoordinateSystem.BasisZ.CrossProduct(connector2.CoordinateSystem.BasisZ).GetLength(), tol)) // 0.001ft = 0.3048 mm 어느 정도 2개 객체가 수평하면 { try { List lstElement2BeDeleted = new List(); Resolver resolver = new Resolver(m_rvtDoc); resolver.JoinPipeSegment(connector1, ref connector2, ref lstElement2BeDeleted); if (lstElement2BeDeleted.Count > 0) { foreach (var el in lstElement2BeDeleted) { m_rvtDoc.Delete(el.Id); } } } catch (Exception ex) { MessageBox.Show(ex.Message); } } else { try { fii = m_rvtDoc.Create.NewElbowFitting(connector1, connector2); } catch (ArgumentNullException ex) { MessageBox.Show(ex.Message); } catch (ArgumentException ex) { MessageBox.Show(ex.Message); } catch (InvalidOperationException ex) { MessageBox.Show(ex.Message); } catch(Exception ex){ MessageBox.Show(ex.Message); } } } } /// /// Copy parameters from source pipe to target pipe. /// /// Coping source /// Coping target private void CopyParameters(Pipe source, Pipe target) { double diameter = source.get_Parameter(BuiltInParameter.RBS_PIPE_DIAMETER_PARAM).AsDouble(); target.get_Parameter(BuiltInParameter.RBS_PIPE_DIAMETER_PARAM).Set(diameter); } /// /// Filter the inputting References, just allow Pipe, Duct and Beam References. /// /// Pipe /// References to filter private void Filter(Pipe pipe, List refs) { for (int i = refs.Count - 1; i >= 0; i--) { Reference cur = refs[i].GetReference(); Element curElem = m_rvtDoc.GetElement(cur); if (curElem.Id == pipe.Id || (!(curElem is Pipe) && curElem.Category.Id.IntegerValue != (int)BuiltInCategory.OST_StructuralFraming)) { refs.RemoveAt(i); } } } private Line FindParallelLine(Section section, Autodesk.Revit.DB.XYZ dir, double height) { Autodesk.Revit.DB.XYZ detal = dir * height; Line line = Line.CreateBound(section.Start + detal, section.End + detal); return line; } /// /// Find out two References, whose ProximityParameter is negative or positive, /// And Get the minimal value from all positive reference, and get the maximal value /// from the negative reference. if there are no such reference, using null instead. /// /// References /// Reference array private ReferenceWithContext[] GetClosestSectionsToOrigin(List refs) { ReferenceWithContext[] mins = new ReferenceWithContext[2]; if (refs.Count == 0) { return mins; } if (refs[0].Proximity > 0) { mins[1] = refs[0]; return mins; } for (int i = 0; i < refs.Count - 1; i++) { if (refs[i].Proximity < 0 && refs[i + 1].Proximity > 0) { mins[0] = refs[i]; mins[1] = refs[i + 1]; return mins; } } mins[0] = refs[refs.Count - 1]; return mins; } public void MepSystem() { Document doc = m_rvtDoc; FilteredElementCollector systemCollector = new FilteredElementCollector(doc).OfClass(typeof(MEPSystem)); IEnumerable desirableSystems = systemCollector.ToElements(); foreach (Element elm in desirableSystems) { MEPSystem system = elm as MEPSystem; if (system != null) { if (system.GetType() == typeof(Autodesk.Revit.DB.Plumbing.PipingSystem)) { Autodesk.Revit.DB.Plumbing.PipeSystemType type = (system as Autodesk.Revit.DB.Plumbing.PipingSystem).SystemType; } string name = system.Name; } } } public Pipe CreateNewPipe(Document document, ElementId systemTypeId, ElementId levelId) { Pipe pipe = null; PipeType pipeType = null; // find a pipe type var pipeTypes = new FilteredElementCollector(document).OfClass(typeof(PipeType)).ToElements(); if (pipeTypes.Count > 0) { pipeType = pipeTypes[pipeTypes.Count - 1] as PipeType; } if (pipeType == null) return null; // create pipe between 2 points XYZ p1 = new XYZ(0, 0, 0); XYZ p2 = new XYZ(10, 0, 0); pipe = Pipe.Create(document, systemTypeId, pipeType.Id, levelId, p1, p2); return pipe; } /// /// 주어진 점에서 가장 가까운 연결점을 찾는다. /// /// /// /// private Connector FindConnector(Pipe pipe, Autodesk.Revit.DB.XYZ conXYZ) { ConnectorSet conns = pipe.ConnectorManager.Connectors; double minDst = double.MaxValue; Connector closestConnector = null; foreach (Connector conn in conns) { if (conn.Origin.DistanceTo(conXYZ) < minDst) { minDst = conn.Origin.DistanceTo(conXYZ); closestConnector = conn; } } return closestConnector; } /// /// 주어진 점에서 가장 가까운 연결점을 찾는다. /// /// /// /// private Connector FindConnector(FlexPipe pipe, Autodesk.Revit.DB.XYZ conXYZ) { ConnectorSet conns = pipe.ConnectorManager.Connectors; double minDst = double.MaxValue; Connector closestConnector = null; foreach (Connector conn in conns) { if (conn.Origin.DistanceTo(conXYZ) < minDst) { minDst = conn.Origin.DistanceTo(conXYZ); closestConnector = conn; } } return closestConnector; } /// /// Find out the connector which the pipe's specified connector connected to. /// The pipe's specified connector is given by point conxyz. /// /// Pipe to find the connector /// Specified point /// Connector whose origin is conXYZ private Connector FindConnectedTo(Pipe pipe, Autodesk.Revit.DB.XYZ conXYZ) { Connector connItself = FindConnector(pipe, conXYZ); ConnectorSet connSet = connItself.AllRefs; foreach (Connector conn in connSet) { if (conn.Owner.Id.IntegerValue != pipe.Id.IntegerValue && conn.ConnectorType == ConnectorType.End) { return conn; } } return null; } } }