Client/Desktop/KMBIM3.0/23.10.16/Cmd/DrainConnect/DrainConnect.cs

1278 lines
58 KiB
C#

using Autodesk.Revit.ApplicationServices;
using Autodesk.Revit.Creation;
using Autodesk.Revit.DB;
using Autodesk.Revit.DB.Plumbing;
using Autodesk.Revit.DB.Structure;
using Autodesk.Revit.UI;
using Autodesk.Revit.UI.Selection;
using KDCS.Utils;
using KMBIM.Revit.Tools;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using View = Autodesk.Revit.DB.View;
namespace KMBIM
{
[Autodesk.Revit.Attributes.Transaction(Autodesk.Revit.Attributes.TransactionMode.Manual)]
public class DrainConnect : IExternalCommand
{
UIApplication uiapp;
UIDocument uidoc;
Autodesk.Revit.DB.Document doc;
Autodesk.Revit.Creation.Application creApp;
Autodesk.Revit.Creation.Document creDoc;
Autodesk.Revit.UI.ExternalCommandData m_commandData;
public Autodesk.Revit.UI.Result Execute(ExternalCommandData commandData, ref string message, ElementSet elements)
{
m_commandData = commandData;
uiapp = commandData.Application;
uidoc = uiapp.ActiveUIDocument;
doc = uidoc.Document;
creApp = uiapp.Application.Create;
creDoc = doc.Create;
try
{
if (!WorkMain.GetInstance().IsValid) return Autodesk.Revit.UI.Result.Succeeded;
List<FamilySymbol> WyeFamSymLst = new List<FamilySymbol>();
//콤보박스에 뿌릴 Y 패밀리 심볼 리스트 구하기
ICollection<Element> WyeCollector = new FilteredElementCollector(doc).OfClass(typeof(FamilySymbol)).ToElements();
foreach (Element e in WyeCollector)
{
FamilySymbol FamSym = e as FamilySymbol;
if (FamSym == null) continue;
bool b_Wye = Util.GetFamilyPartType(FamSym, PartType.Wye);
if (b_Wye)
{
WyeFamSymLst.Add(e as FamilySymbol);
}
}
Form_DrainConnect dlg = new Form_DrainConnect();
dlg.YFamSymLst = WyeFamSymLst;
dlg.document = doc;
if (dlg.ShowDialog() == DialogResult.Cancel) return Result.Cancelled;
int Connect_Type = dlg.Rad_Idx;
double m_D1 = dlg.m_D1;
string ComboFamName = dlg.m_YFamName;
FamilySymbol YFamSymbol = null;
foreach (FamilySymbol sym in WyeFamSymLst)
{
if (ComboFamName == sym.FamilyName)
YFamSymbol = sym;
}
//부착할 메인 배관 선택
Reference pickRef = commandData.Application.ActiveUIDocument.Selection.PickObject
(ObjectType.Element, new PipeSelectionFilter(), "배수관 연결할 메인관 선택 : ");
Pipe pickPipe = doc.GetElement(pickRef) as Pipe;
//위생기기 선택
IList<Reference> pickRefs = commandData.Application.ActiveUIDocument.Selection.PickObjects
(ObjectType.Element, new HygienicSelectionFilter(), "위생기기 선택 : ");
//패밀리 리스트로 저장
List<FamilyInstance> FamLst = new List<FamilyInstance>();
foreach (Reference refer in pickRefs)
{
Element elem = doc.GetElement(refer);
if (elem is FamilyInstance)
FamLst.Add(elem as FamilyInstance);
}
//패밀리 중 가장 아래쪽 커넥터 리스트 저장
List<Connector> bottomConLst = new List<Connector>();
foreach (FamilyInstance fam in FamLst)
{
Connector bottomCon = GetBottonConnector(fam);
if (bottomCon != null)
bottomConLst.Add(bottomCon);
}
////메인관 offset 값 > 가장 아래쪽 커넥터 + 65mm 면 오류메시지 후 종료
//foreach (Connector con in bottomConLst)
//{
// //65mm 는 관경100 배관 엘보 길이
// if (pickPipe.LevelOffset > con.Origin.Z - Unit.MMToFeet(65))
// {
// MessageBox.Show("연결할 메인배관과 위생기구 사이의 거리가 좁습니다.", "오류");
// return Result.Cancelled;
// }
//}
XYZ DirPt = pickRef.GlobalPoint;
XYZ sp = null, ep = null;
Util.GetStartEndPoint(doc.GetElement(pickRef) as Pipe, ref sp, ref ep);
XYZ dirpt = (sp - ep).Normalize();
//T,YT에 따른 작업
if (Connect_Type == 0)
TeeType1Process(pickPipe, DirPt, bottomConLst);
else if (Connect_Type == 1)
TeeType2Process(pickPipe, dirpt, m_D1, bottomConLst);
else if (Connect_Type == 2)
YTeeType1Process(pickPipe, m_D1, DirPt, bottomConLst, YFamSymbol);
else if (Connect_Type == 3)
YTeeType2Process(pickPipe, m_D1, DirPt, bottomConLst, YFamSymbol);
}
catch (Exception e)
{
//MessageBox.Show("" + e);
}
return Result.Succeeded;
}
/// <summary>
/// YTee 타입1 연결 프로세스
/// </summary>
/// <param name="mainPipe"></param> 메인관
/// <param name="bottomConLst"></param> 위생기기 가장 아래쪽 커넥터
void TeeType1Process(Pipe mainPipe, XYZ Dirpt, List<Connector> bottomConLst)
{
ElementId PipeSystemTypeId = mainPipe.MEPSystem.GetTypeId();
ElementId PipeTypeId = mainPipe.PipeType.Id;
ElementId ViewId = mainPipe.ReferenceLevel.Id;
int idx_Error1 = 0, idx_Error2 = 0, idx_Error3 = 0;
XYZ mainSp = null, mainEp = null;
Util.GetStartEndPoint(mainPipe, ref mainSp, ref mainEp);
bool isDir = false;
//중간점에서
if (Dirpt.DistanceTo(mainEp) < Dirpt.DistanceTo(mainSp))
isDir = true;
try
{
using (Transaction trans = new Transaction(doc))
{
trans.Start("t");
List<XYZ> pts = new List<XYZ>();
//foreach (Connector con in bottomConLst)
for (int i = 0; i < bottomConLst.Count; i++)
{
pts.Clear();
Connector con = bottomConLst[i];
Line mainLine = (mainPipe.Location as LocationCurve).Curve as Line;
IntersectionResult interRes = mainLine.Project(con.Origin);
if (interRes == null) continue;
//메인과 커넥터 교차점
XYZ MainInterPt = interRes.XYZPoint;
//위생기기 커넥터와 메인배관 가까운 점이 메인배관의 시작 또는 끝 점 일 경우
if (MainInterPt.IsAlmostEqualTo(mainSp) || MainInterPt.IsAlmostEqualTo(mainEp))
{
idx_Error1++;
continue;
}
//위생기기 커넥터 높이가 메인배관 교차점+여유길이보다 같거나 낮을 때
if (MainInterPt.Z + Unit.MMToFeet(79) > con.Origin.Z)
{
idx_Error2++;
continue;
}
XYZ pipeEpt = Util.Polar(con.Origin, con.Origin,
new XYZ(con.Origin.X, con.Origin.Y, con.Origin.Z - Unit.MMToFeet(1)), Math.Abs(MainInterPt.Z - con.Origin.Z));
//Util.Pyosi(doc, pipeEpt, 0);
//Util.Pyosi(doc, MainInterPt, 0);
if (pipeEpt.DistanceTo(MainInterPt) < Unit.MMToFeet(140))
{
idx_Error3++;
continue;
}
Parameter paramMainDia = mainPipe.get_Parameter(BuiltInParameter.RBS_PIPE_DIAMETER_PARAM);
pts.Add(con.Origin);
pts.Add(pipeEpt);
pts.Add(MainInterPt);
//배관 생성
List<Pipe> pipeLst = Util.CreatePipe2(uiapp, doc, pts, con.Radius * 2, PipeSystemTypeId, PipeTypeId, ViewId);
//T연결
Pipe LastPipe = pipeLst.Last();
Connector spCon = null, epCon = null;
Util.GetStartEndConnector(LastPipe, ref spCon, ref epCon);
List<ElementId> divLst = Util.DivideElement(doc, mainPipe, epCon.Origin);
doc.Regenerate();
List<Connector> crossConLst = new List<Connector>();
foreach (ElementId id in divLst)
{
//Pipe divPipe = doc.GetElement(id) as Pipe;
List<Connector> ConLst = Util.GetElementConnectors(doc.GetElement(id));
foreach (Connector DivCon in ConLst)
{
if (epCon.Origin.IsAlmostEqualTo(DivCon.Origin))
crossConLst.Add(DivCon);
}
}
FamilyInstance tee = null;
if (isDir == true)
tee = doc.Create.NewTeeFitting(crossConLst.Last(), crossConLst.First(), epCon);
else
tee = doc.Create.NewTeeFitting(crossConLst.First(), crossConLst.Last(), epCon);
doc.Regenerate();
//자른 파이프로 변경
mainPipe = doc.GetElement(divLst.First()) as Pipe;
//자른 파이프가 다음 커넥터와 교차점이 없으면 다음 자른파이프로 변경
if (bottomConLst.Count > i + 1)
{
Line divLine = (mainPipe.Location as LocationCurve).Curve as Line;
Connector cons = bottomConLst[i + 1];
IntersectionResult intersection = divLine.Project(cons.Origin);
if (intersection != null)
{
XYZ interpt = intersection.XYZPoint;
List<Connector> divMainConLst = Util.GetElementConnectors(mainPipe);
foreach (Connector mainCon in divMainConLst)
{
if (interpt.IsAlmostEqualTo(mainCon.Origin))
{
mainPipe = doc.GetElement(divLst.Last()) as Pipe;
break;
}
}
}
}
}
trans.Commit();
string error_str = "";
if (idx_Error1 != 0)
error_str += idx_Error1 + "개의 위생기기와 메인배관이 교차하지 않습니다.";
if (idx_Error2 != 0)
error_str += "\n" + idx_Error2 + "개의 위생기기와 배관사이 연결할 높이가 좁습니다.";
if (idx_Error3 != 0)
error_str += "\n" + idx_Error3 + "개의 위생기기와 배관사이 연결할 거리가 좁습니다.";
if (error_str != "")
MessageBox.Show(error_str, "오류");
}
}
catch (Exception e)
{
// MessageBox.Show("" + e);
}
}
/// <summary>
/// YTee 타입2 연결 프로세스
/// </summary>
/// <param name="mainPipe"></param> 메인관
/// <param name="bottomConLst"></param> 위생기기 가장 아래쪽 커넥터
void TeeType2Process(Pipe mainPipe, XYZ Dirpt, double dist_D1, List<Connector> bottomConLst)
{
ElementId PipeSystemTypeId = mainPipe.MEPSystem.GetTypeId();
ElementId PipeTypeId = mainPipe.PipeType.Id;
ElementId ViewId = mainPipe.ReferenceLevel.Id;
int idx_Error1 = 0, idx_Error2 = 0;
XYZ mainSp = null, mainEp = null;
Util.GetStartEndPoint(mainPipe, ref mainSp, ref mainEp);
bool isDir = false;
//중간점에서
if (Dirpt.DistanceTo(mainEp) < Dirpt.DistanceTo(mainSp))
isDir = true;
try
{
using (Transaction trans = new Transaction(doc))
{
trans.Start("t");
List<XYZ> pts = new List<XYZ>();
//foreach (Connector con in bottomConLst)
for (int i = 0; i < bottomConLst.Count; i++)
{
pts.Clear();
Connector con = bottomConLst[i];
Line mainLine = (mainPipe.Location as LocationCurve).Curve as Line;
IntersectionResult interRes = mainLine.Project(con.Origin);
if (interRes == null) continue;
//메인과 커넥터 교차점
XYZ MainInterPt = interRes.XYZPoint;
//D1 길이만큼 아래로 이동한 점
XYZ dist1pt = new XYZ(con.Origin.X, con.Origin.Y, con.Origin.Z - Unit.MMToFeet(1));
XYZ pipeD1pt = Util.Polar(con.Origin, con.Origin, dist1pt, Unit.MMToFeet(dist_D1));
XYZ pipeMainZpt = Util.Polar(con.Origin, con.Origin, pipeD1pt, con.Origin.Z - MainInterPt.Z);
//Util.Pyosi(doc, pipeMainZpt, 0);
//메인관 교차점과 D1pt 벡터 방향
XYZ interZD1pt = new XYZ(MainInterPt.X, MainInterPt.Y, pipeD1pt.Z);
XYZ vec = (interZD1pt - pipeD1pt).Normalize();
// D1+엘보 여유분이 위생기기-메인관 거리보다 클 경우
if (con.Origin.DistanceTo(pipeMainZpt) <= Unit.MMToFeet(dist_D1 + 100))
{
idx_Error1++;
//MessageBox.Show("위생기기와 배관사이 연결할 거리가 좁습니다.", "오류");
continue;
}
// 45도 절단점이 메인관 시작 또는 끝점일 경우 작업 취소
if (MainInterPt.IsAlmostEqualTo(mainSp) || MainInterPt.IsAlmostEqualTo(mainEp))
{
idx_Error2++;
continue;
}
pts.Add(con.Origin);
pts.Add(pipeD1pt);
pts.Add(interZD1pt);
pts.Add(MainInterPt);
//파이프 생성
List<Pipe> pipeLst = Util.CreatePipe2(uiapp, doc, pts, con.Radius * 2, PipeSystemTypeId, PipeTypeId, ViewId);
//T연결
Pipe LastPipe = pipeLst.Last();
Connector spCon = null, epCon = null;
Util.GetStartEndConnector(LastPipe, ref spCon, ref epCon);
List<ElementId> divLst = Util.DivideElement(doc, mainPipe, epCon.Origin);
doc.Regenerate();
List<Connector> crossConLst = new List<Connector>();
foreach (ElementId id in divLst)
{
//Pipe divPipe = doc.GetElement(id) as Pipe;
List<Connector> ConLst = Util.GetElementConnectors(doc.GetElement(id));
foreach (Connector DivCon in ConLst)
{
if (epCon.Origin.IsAlmostEqualTo(DivCon.Origin))
crossConLst.Add(DivCon);
}
}
FamilyInstance tee = null;
if (isDir == true)
tee = doc.Create.NewTeeFitting(crossConLst.Last(), crossConLst.First(), epCon);
else
tee = doc.Create.NewTeeFitting(crossConLst.First(), crossConLst.Last(), epCon);
doc.Regenerate();
//자른 파이프로 변경
mainPipe = doc.GetElement(divLst.First()) as Pipe;
//자른 파이프가 다음 커넥터와 교차점이 없으면 다음 자른파이프로 변경
if (bottomConLst.Count > i + 1)
{
Line divLine = (mainPipe.Location as LocationCurve).Curve as Line;
Connector cons = bottomConLst[i + 1];
IntersectionResult intersection = divLine.Project(cons.Origin);
if (intersection != null)
{
XYZ interpt = intersection.XYZPoint;
List<Connector> divMainConLst = Util.GetElementConnectors(mainPipe);
foreach (Connector mainCon in divMainConLst)
{
if (interpt.IsAlmostEqualTo(mainCon.Origin))
{
mainPipe = doc.GetElement(divLst.Last()) as Pipe;
break;
}
}
}
}
}
trans.Commit();
string error_str = "";
if (idx_Error1 != 0)
error_str += idx_Error1 + "개의 위생기기와 배관사이 연결할 거리가 좁습니다.";
else if (idx_Error2 != 0)
error_str += "\n" + idx_Error2 + "개의 파이프와 메인배관이 교차하지 않습니다.";
if (error_str != "")
MessageBox.Show(error_str, "오류");
}
}
catch
{
}
}
/// <summary>
/// Y관 타입1 연결 프로세스
/// </summary>
/// <param name="mainPipe"></param> 메인관
/// <param name="m_YTdist"></param> 위생기기-메인관 직교 길이
/// <param name="bottomConLst"></param> 위생기기기 가장 아래쪽 커넥터
void YTeeType1Process(Pipe mainPipe, double dist_D1, XYZ Dirpt, List<Connector> bottomConLst, FamilySymbol Ysymbol)
{
ElementId PipeSystemTypeId = mainPipe.MEPSystem.GetTypeId();
ElementId PipeTypeId = mainPipe.PipeType.Id;
ElementId ViewId = mainPipe.ReferenceLevel.Id;
int idx_Error1 = 0, idx_Error2 = 0;
XYZ Sp = null, Ep = null;
Util.GetStartEndPoint(mainPipe, ref Sp, ref Ep);
bool isDir = false;
//중간점에서
if (Dirpt.DistanceTo(Ep) < Dirpt.DistanceTo(Sp))
isDir = true;
XYZ mainVec = null;
if (isDir == true)
mainVec = (Sp - Ep).Normalize();
else
mainVec = (Ep - Sp).Normalize();
try
{
List<XYZ> pts = new List<XYZ>();
List<ElementId> dividePipeLst = new List<ElementId>();
Line mainLine = null;
using (Transaction trans = new Transaction(doc))
{
trans.Start("start");
for (int i = 0; i < bottomConLst.Count; i++)
{
pts.Clear();
Connector con = bottomConLst[i];
mainLine = (mainPipe.Location as LocationCurve).Curve as Line;
XYZ mainSp = null, mainEp = null;
Util.GetStartEndPoint(mainPipe, ref mainSp, ref mainEp);
//Util.Pyosi(doc, mainSp, 1);
//MessageBox.Show("1");
IntersectionResult interRes = mainLine.Project(con.Origin);
if (interRes == null) continue;
//메인과 커넥터 교차점
XYZ MainInterPt = interRes.XYZPoint;
XYZ pipeEpt = Util.Polar(con.Origin, con.Origin,
new XYZ(con.Origin.X, con.Origin.Y, con.Origin.Z - Unit.MMToFeet(1)), Math.Abs(MainInterPt.Z - con.Origin.Z));
//Util.Pyosi(doc, pipeEpt, 1);
//D1+엘보 여유분이 위생기기-메인관 거리보다 클 경우
if (pipeEpt.DistanceTo(MainInterPt) <= Unit.MMToFeet(dist_D1 + 100))
{
//위생기기와 메인배관 사이 거리가 좁습니다.
idx_Error1++;
continue;
}
pts.Add(con.Origin);
pts.Add(pipeEpt);
interRes = mainLine.Project(pipeEpt);
//메인관과 배관 끝 꾜차점
XYZ mainDivPt = interRes.XYZPoint;
XYZ vec = (mainDivPt - pipeEpt).Normalize();
//D1 길이 이동 점
XYZ D1pt = Util.Polar(pipeEpt, vec, Unit.MMToFeet(dist_D1));
pts.Add(D1pt);
//Util.Pyosi(doc, D1pt, 0);
//List<Pipe> pipeLst = Util.CreatePipe2(uiapp, doc, pts, con.Radius * 2, mainPipe.MEPSystem.GetTypeId(), mainPipe.PipeType.Id, mainPipe.ReferenceLevel.Id);
//if (isDir == true)
XYZ newVec = (D1pt - pipeEpt).Normalize();
XYZ RevNewVec = (pipeEpt - D1pt).Normalize();
XYZ rotVec1 = Util.RotateVector(newVec, Util.DTR(45));
XYZ plrpt1 = Util.Polar(D1pt, rotVec1, 200);
XYZ rotVec2 = Util.RotateVector(newVec, Util.DTR(-45));
XYZ plrpt2 = Util.Polar(D1pt, rotVec2, 200);
XYZ interPt1 = null, interPt2 = null;
interRes = mainLine.Project(plrpt1);
if (interRes == null)
continue;
interPt1 = interRes.XYZPoint;
interRes = mainLine.Project(plrpt2);
if (interRes == null)
continue;
interPt2 = interRes.XYZPoint;
//배관 시작-끝 방향인지
XYZ isDirPoint = null;
if (isDir == true)
isDirPoint = mainEp;
else
isDirPoint = mainSp;
//Util.Pyosi(doc, isDirPoint, 0);
XYZ Cur45Pt = null;
if (isDirPoint.DistanceTo(interPt1) < isDirPoint.DistanceTo(interPt2))
Cur45Pt = plrpt1;
else
Cur45Pt = plrpt2;
//45도 라인과와 메인관 라인의 교차점(45피팅 절단점 찾기)
XYZ inter45pt = Util.GetIntersectionPoint(mainSp, mainEp, D1pt, Cur45Pt);
XYZ LineOnPt = Util.GetPointOnLine(mainLine, inter45pt);
if (LineOnPt.IsAlmostEqualTo(mainEp) || LineOnPt.IsAlmostEqualTo(mainSp))
{
idx_Error2++;
//MessageBox.Show("메인관에 45도 파이프가 교차하지 않습니다.", "오류");
continue;
}
//Util.Pyosi(doc, aa, 0);
pts.Add(inter45pt);
List<Pipe> pipeLst = new List<Pipe>();
//위생기기-45도 파이프까지 생성
//List<Pipe> pipeLst = Util.CreatePipe2(uiapp, doc, pts, con.Radius * 2, PipeSystemTypeId, PipeTypeId, ViewId);
pipeLst = Util.CreatePipe2(uiapp, doc, pts, con.Radius * 2, PipeSystemTypeId, PipeTypeId, ViewId);
Pipe LastPipe = pipeLst.Last();
Connector spCon = null, epCon = null;
Util.GetStartEndConnector(LastPipe, ref spCon, ref epCon);
Curve LastPipeLine = (LastPipe.Location as LocationCurve).Curve;
FamilyInstance yTeeFam = null;
if (Ysymbol.IsActive == false)
Ysymbol.Activate();
//FamilyInstance yTeeFam = uidoc.Document.Create.NewFamilyInstance(inter45pt, Ysymbol, mainVec, LastPipe, StructuralType.NonStructural);
yTeeFam = uidoc.Document.Create.NewFamilyInstance(inter45pt, Ysymbol, mainVec, LastPipe, StructuralType.NonStructural);
doc.Regenerate();
//y관 메인 커넥터 관경 변경
Definition df = GetDefinition(yTeeFam, "NomRad1");
Parameter param = yTeeFam.get_Parameter(df);
if (param != null)
param.Set(mainPipe.Diameter / 2.0);
//y관 가지 커넥터 관경 변경
df = GetDefinition(yTeeFam, "NomRad2");
param = yTeeFam.get_Parameter(df);
if (param != null)
param.Set(LastPipe.Diameter / 2.0);
doc.Regenerate();
//45도 파이프 시작-끝 방향 벡터
XYZ Vec_45Pipe = (epCon.Origin - spCon.Origin).Normalize();
List<Connector> branchConLst = Util.GetBranchConnector(yTeeFam, mainPipe);
//List<Connector> branchConLst = Util.GetBranchConnector(yTeeFam);
//삽입된 Y관Tee의 45도커넥터-Y관삽입점 방향 벡터
XYZ Vec_45TeeCon = ((yTeeFam.Location as LocationPoint).Point - branchConLst.First().Origin).Normalize();
//Y관 가지커넥터의 위치가 메인관의 오른쪽인가
bool b_BranchConRight = Util.isRightPoint(branchConLst.First().Origin, mainSp, mainEp);
//45도 배관 시작점 위치가 메인관의 오른쪽인가
bool b_MainRight = Util.isRightPoint(spCon.Origin, mainSp, mainEp);
//패밀리 삽입점
XYZ FamLoc = (yTeeFam.Location as LocationPoint).Point;
//위의 Y관 가지커넥터, 45도 배관 시작점 둘 중 하나가 오른쪽이 아니면 Mirror로 변경
if (b_BranchConRight != b_MainRight)
{
//45도벡터와 메인과 벡터 교차로 z벡터구하기
XYZ crossVec1 = Vec_45TeeCon.CrossProduct(mainVec);
//메인 벡터와 z벡터로 x또는y벡터 구하기
XYZ crossVec2 = mainVec.CrossProduct(crossVec1);
Plane plane = Plane.CreateByNormalAndOrigin(crossVec2, FamLoc);
//Y관 대칭
ElementTransformUtils.MirrorElements(doc, new List<ElementId> { yTeeFam.Id }, plane, false);
doc.Regenerate();
}
XYZ plr45pt = Util.Polar(epCon.Origin, epCon.Origin, spCon.Origin, spCon.Origin.DistanceTo(epCon.Origin) / 2.0);
//Util.Pyosi(doc, plr45pt, 0);
IntersectionResult interResult = mainLine.Project(plr45pt);
if (interResult != null)
{
XYZ interpt = interResult.XYZPoint;
XYZ interVec = (plr45pt - interpt).Normalize();
XYZ RevInterVec = interVec.Negate();
XYZ VecPt = Util.Polar(FamLoc, interVec, 500);
XYZ ReVecPt = Util.Polar(FamLoc, RevInterVec, 500);
//Y관 가지커넥터의 위치가 메인관의 오른쪽인가
bool b_Vir_BranchConRight = Util.isRightPoint(branchConLst.First().Origin, VecPt, ReVecPt);
//45도 배관 시작점 위치가 메인관의 오른쪽인가
bool b_Vir_MainRight = Util.isRightPoint(spCon.Origin, VecPt, ReVecPt);
if (b_Vir_BranchConRight != b_Vir_MainRight)
{
Plane plane = Plane.CreateByNormalAndOrigin(mainVec, FamLoc);
ElementTransformUtils.MirrorElements(doc, new List<ElementId> { yTeeFam.Id }, plane, false);
doc.Regenerate();
}
}
List<ElementId> divLst = new List<ElementId>();
//메인관 자르기
divLst = Util.DivideElement(doc, mainPipe, inter45pt);
if (divLst.Count == 0) continue;
foreach (ElementId id in divLst)
{
bool b_contain = dividePipeLst.Contains(id);
if (b_contain == false) dividePipeLst.Add(id);
}
doc.Regenerate();
//자른 파이프로 변경
//mainPipe = doc.GetElement(divLst.First()) as Pipe;
//자른 파이프 리스트에서 교차점이 있는 파이프 메인파이프로 변경
for (int j = 0; j < dividePipeLst.Count; j++)
{
if (bottomConLst.Count > i + 1)
{
Line divLine = (mainPipe.Location as LocationCurve).Curve as Line;
Connector cons = bottomConLst[i + 1];
IntersectionResult intersection = divLine.Project(cons.Origin);
if (intersection != null)
{
XYZ interpt = intersection.XYZPoint;
List<Connector> divMainConLst = Util.GetElementConnectors(mainPipe);
foreach (Connector mainCon in divMainConLst)
{
if (interpt.IsAlmostEqualTo(mainCon.Origin))
{
mainPipe = doc.GetElement(dividePipeLst[j]) as Pipe;
break;
}
}
}
}
}
//잘린 파이프 리스트에 45도 파이프 추가
List<ElementId> YTeeConPipeLst = new List<ElementId>();
YTeeConPipeLst.AddRange(divLst);
YTeeConPipeLst.Add(LastPipe.Id);
//divLst.Add(LastPipe.Id);
foreach (ElementId id in YTeeConPipeLst)
{
Pipe Connect45Pipe = doc.GetElement(id) as Pipe;
//파이프 커넥터
List<Connector> conLst = Util.GetElementConnectors(Connect45Pipe);
foreach (Connector PipeCon in conLst)
{
//Y관 패밀리 삽입점과 같은 커넥터 찾기
if (PipeCon.Origin.IsAlmostEqualTo(FamLoc))
{
//파이프 역방향 벡터 구하기
XYZ LineDirReVec = null;
Connector pipeSpCon = null, pipeEpCon = null;
Util.GetStartEndConnector(PipeCon.Owner as Pipe, ref pipeSpCon, ref pipeEpCon);
if (PipeCon.Origin.IsAlmostEqualTo(pipeSpCon.Origin))
LineDirReVec = (pipeEpCon.Origin - pipeSpCon.Origin).Normalize();
else
LineDirReVec = (pipeSpCon.Origin - pipeEpCon.Origin).Normalize();
//파이프 역방향으로 50mm이동한 점
XYZ plr100Pt = Util.Polar(PipeCon.Origin, LineDirReVec, Unit.MMToFeet(100));
//파이프 커넥터 위치 변경
PipeCon.Origin = plr100Pt;
//파이프 커넥터와 가까운 Y관 커넥터 구하기.
double minDist = double.MaxValue;
Connector closeCon = null;
List<Connector> yFamConLst = Util.GetElementConnectors(yTeeFam);
foreach (Connector yFamCon in yFamConLst)
{
if (yFamCon.Origin.DistanceTo(plr100Pt) < minDist)
{
minDist = yFamCon.Origin.DistanceTo(plr100Pt);
closeCon = yFamCon;
}
}
//파이프 커넥터 위치를 Y관 커넥터 위치로 변경
PipeCon.Origin = closeCon.Origin;
//파이프 커넥퇑 Y관 커넥터 연결
PipeCon.ConnectTo(closeCon);
break;
}
}
}
}
trans.Commit();
}
string error_str = "";
if (idx_Error1 != 0)
error_str += idx_Error1 + "개의 위생기기와 메인배관 사이 거리가 좁습니다.";
if (idx_Error2 != 0)
error_str += "\n" + idx_Error2 + "개의 45도 파이프가 메인관에 교차하지 않습니다.";
if (error_str != "")
MessageBox.Show(error_str, "오류");
}
catch (Exception e)
{
//MessageBox.Show("" + e);
}
}
/// <summary>
/// Y관 타입2 연결 프로세스
/// </summary>
/// <param name="mainPipe"></param> 메인관
/// <param name="m_YTdist"></param> 위생기기-메인관 직교 길이
/// <param name="bottomConLst"></param> 위생기기기 가장 아래쪽 커넥터
void YTeeType2Process(Pipe mainPipe, double dist_D1, XYZ Dirpt, List<Connector> bottomConLst, FamilySymbol Ysymbol)
{
ElementId PipeSystemTypeId = mainPipe.MEPSystem.GetTypeId();
ElementId PipeTypeId = mainPipe.PipeType.Id;
ElementId ViewId = mainPipe.ReferenceLevel.Id;
int idx_Error1 = 0, idx_Error2 = 0, idx_Error3 = 0, idx_Error4 = 0;
string str_Error4 = "";
double chg_MainDia = 0, chg_SubDia = 0;
XYZ Sp = null, Ep = null;
Util.GetStartEndPoint(mainPipe, ref Sp, ref Ep);
bool isDir = false;
//중간점에서
if (Dirpt.DistanceTo(Ep) < Dirpt.DistanceTo(Sp))
isDir = true;
XYZ mainVec = null;
if (isDir == true)
mainVec = (Sp - Ep).Normalize();
else
mainVec = (Ep - Sp).Normalize();
using (Transaction transGruop = new Transaction(doc))
{
transGruop.Start("Start");
try
{
List<XYZ> pts = new List<XYZ>();
List<ElementId> dividePipeLst = new List<ElementId>();
Line mainLine = null;
for (int i = 0; i < bottomConLst.Count; i++)
{
pts.Clear();
Connector con = bottomConLst[i];
mainLine = (mainPipe.Location as LocationCurve).Curve as Line;
XYZ mainSp = null, mainEp = null;
Util.GetStartEndPoint(mainPipe, ref mainSp, ref mainEp);
//메인관 방향 파이프 점 구할 때 파이프 길이가 짧으면 틀어져서 구해지는 현상때문에 확장 라인 생성
XYZ ExtendMainSp = Util.Polar(mainSp, mainEp, mainSp, 200);
XYZ ExtendMainEp = Util.Polar(mainEp, mainSp, mainEp, 200);
Line ExtMainLine = Line.CreateBound(ExtendMainSp, ExtendMainEp);
IntersectionResult interRes = ExtMainLine.Project(con.Origin);
if (interRes == null) continue;
//메인과 커넥터 교차점
XYZ MainInterPt = interRes.XYZPoint;
//D1 길이만큼 아래로 이동한 점
XYZ pipeD1pt = Util.Polar(con.Origin, con.Origin,
new XYZ(con.Origin.X, con.Origin.Y, con.Origin.Z - Unit.MMToFeet(1)), Unit.MMToFeet(dist_D1));
//메인관 교차점과 D1pt 벡터 방향
XYZ interZD1pt = new XYZ(MainInterPt.X, MainInterPt.Y, pipeD1pt.Z);
XYZ connectVec = (interZD1pt - pipeD1pt).Normalize();
// D1+엘보 여유분이 위생기기-메인관 거리보다 클 경우
if (pipeD1pt.DistanceTo(interZD1pt) <= Unit.MMToFeet(dist_D1 + 100))
{
//위생기기와 메인배관 사이 거리가 좁습니다.
idx_Error1++;
continue;
}
pts.Add(con.Origin);
pts.Add(pipeD1pt);
pts.Add(interZD1pt);
//파이프 생성
//List<Pipe> pipeLst = Util.CreatePipe2(uiapp, doc, pts, con.Radius * 2, PipeSystemTypeId, PipeTypeId, ViewId);
XYZ newVec = (pipeD1pt - con.Origin).Normalize();
XYZ RevNewVec = (con.Origin - pipeD1pt).Normalize();
XYZ rotXVec1 = Util.RotateVectorX(newVec, Util.DTR(45));
XYZ plrpt1 = Util.Polar(interZD1pt, rotXVec1, 200);
XYZ rotXVec2 = Util.RotateVectorX(newVec, Util.DTR(-45));
XYZ plrpt2 = Util.Polar(interZD1pt, rotXVec2, 200);
ExtendMainSp = Util.Polar(mainSp, mainEp, mainSp, 200);
ExtendMainEp = Util.Polar(mainEp, mainSp, mainEp, 200);
XYZ interpt1 = null, interpt2 = null;
//45도 라인과 메인관 라인의 교차점(45피팅 절단점 찾기)
Line line1 = Line.CreateBound(ExtendMainSp, ExtendMainEp);
Line line2 = Line.CreateBound(interZD1pt, plrpt1);
Line line3 = Line.CreateBound(interZD1pt, plrpt2);
interpt1 = Util.GetCurvesIntersectionPoint(line1, line2);
if (interpt1 == null)
{
XYZ rotYVec1 = Util.RotateVectorY(newVec, Util.DTR(45));
plrpt1 = Util.Polar(interZD1pt, rotYVec1, 200);
line2 = Line.CreateBound(interZD1pt, plrpt1);
interpt1 = Util.GetCurvesIntersectionPoint(line1, line2);
if (interpt1 == null) continue;
}
interpt2 = Util.GetCurvesIntersectionPoint(line1, line3);
if (interpt2 == null)
{
XYZ rotYVec2 = Util.RotateVectorY(newVec, Util.DTR(-45));
plrpt2 = Util.Polar(interZD1pt, rotYVec2, 200);
line3 = Line.CreateBound(interZD1pt, plrpt2);
interpt2 = Util.GetCurvesIntersectionPoint(line1, line3);
if (interpt2 == null) continue;
}
//XYZ LineOnPt = Util.GetPointOnLine(mainLine, interpt1);
//Util.Pyosi(doc, interpt1, 0);
//배관 시작-끝 방향인지
XYZ isDirPoint = null;
if (isDir == true)
isDirPoint = mainEp;
else
isDirPoint = mainSp;
//파이프 선택 시 위치에 따른 45도 절단점 위치 설정
XYZ Cur45Pt = null;
if (isDirPoint.DistanceTo(interpt1) < isDirPoint.DistanceTo(interpt2))
Cur45Pt = interpt1;
else
Cur45Pt = interpt2;
Cur45Pt = Util.GetPointOnLine(mainLine, Cur45Pt);
//45도 절단점이 메인관 시작 또는 끝점일 경우 작업 취소
if (Cur45Pt.IsAlmostEqualTo(mainEp) || Cur45Pt.IsAlmostEqualTo(mainSp))
{
idx_Error2++;
continue;
}
//(45도 점 Z - D1 점 Z) 거리가 100보다 작을 때
if (Math.Abs(pipeD1pt.Z - Cur45Pt.Z) <= Unit.MMToFeet(100))
{
idx_Error3++;
continue;
}
pts.Add(Cur45Pt);
List<Pipe> pipeLst = new List<Pipe>();
Transaction trans = new Transaction(doc);
//trans.Start("1");
//위생기기 -45도 파이프까지 생성
pipeLst = Util.CreatePipe2(uiapp, doc, pts, con.Radius * 2, PipeSystemTypeId, PipeTypeId, ViewId);
doc.Regenerate();
//trans.Commit();
Pipe LastPipe = pipeLst.Last();
Connector spCon = null, epCon = null;
Util.GetStartEndConnector(LastPipe, ref spCon, ref epCon);
Curve lastPipeLine = (LastPipe.Location as LocationCurve).Curve;
FamilyInstance yTeeFam = null;
if (Ysymbol.IsActive == false)
Ysymbol.Activate();
yTeeFam = uidoc.Document.Create.NewFamilyInstance(Cur45Pt, Ysymbol, mainVec, LastPipe, StructuralType.NonStructural);
chg_MainDia = Unit.FeetToMM(mainPipe.Diameter / 2.0);
chg_SubDia = Unit.FeetToMM(LastPipe.Diameter / 2.0);
if (mainPipe.Diameter == Unit.MMToFeet(35)
|| mainPipe.Diameter == Unit.MMToFeet(40)
|| mainPipe.Diameter == Unit.MMToFeet(50)
|| mainPipe.Diameter == Unit.MMToFeet(75)
|| mainPipe.Diameter == Unit.MMToFeet(80)
|| mainPipe.Diameter == Unit.MMToFeet(100)
|| mainPipe.Diameter == Unit.MMToFeet(125))
{
//Y관 메인 커넥터 관경 변경
Definition df = GetDefinition(yTeeFam, "NomRad1");
Parameter param = yTeeFam.get_Parameter(df);
if (param != null)
param.Set(mainPipe.Diameter / 2.0);
}
else
{
//Y관 삭제
Util.Delete(doc, yTeeFam);
idx_Error4++;
string tmp = Unit.FeetToMM(mainPipe.Diameter) + "x"
+ Unit.FeetToMM(LastPipe.Diameter) + " Y관은 지원하지 않습니다.";
bool b_error = str_Error4.Contains(tmp);
if(b_error==false)
str_Error4 += tmp;
//trans.Commit();
continue;
}
if (LastPipe.Diameter == Unit.MMToFeet(35)
|| LastPipe.Diameter == Unit.MMToFeet(40)
|| LastPipe.Diameter == Unit.MMToFeet(50)
|| LastPipe.Diameter == Unit.MMToFeet(75)
|| LastPipe.Diameter == Unit.MMToFeet(80)
|| LastPipe.Diameter == Unit.MMToFeet(100)
|| LastPipe.Diameter == Unit.MMToFeet(125))
{
//Y관 가지 커넥터 관경 변경
Definition df = GetDefinition(yTeeFam, "NomRad2");
Parameter param = yTeeFam.get_Parameter(df);
if (param != null)
param.Set(LastPipe.Diameter / 2.0);
}
else
{
//Y관 삭제
Util.Delete(doc, yTeeFam);
idx_Error4++;
string tmp = Unit.FeetToMM(mainPipe.Diameter) + "x"
+ Unit.FeetToMM(LastPipe.Diameter) + " Y관은 지원하지 않습니다.";
bool b_error = str_Error4.Contains(tmp);
if (b_error == false)
str_Error4 += tmp;
//trans.Commit();
continue;
}
doc.Regenerate();
//trans.Start("3");
//45도 파이프 시작-끝 방향 벡터
XYZ Vec_45Pipe = (epCon.Origin - spCon.Origin).Normalize();
List<Connector> branchConLst = Util.GetBranchConnector(yTeeFam, mainPipe);
//패밀리 삽입점
XYZ FamLoc = (yTeeFam.Location as LocationPoint).Point;
interRes = mainLine.Project(spCon.Origin);
//메인관 교차점과 D1pt방향 벡터와 반대반향 벡터
//XYZ connectVec = (interZD1pt - pipeD1pt).Normalize();
XYZ ReConnectVec = connectVec.Negate();
XYZ VecPt = Util.Polar(FamLoc, connectVec, Unit.MMToFeet(500));
XYZ ReVecPt = Util.Polar(FamLoc, ReConnectVec, Unit.MMToFeet(500));
//Y관 가지 커넥터 위치가 메인관의 오른쪽인가
bool b_Vir_BranchConRIght = Util.isRightPoint(branchConLst.First().Origin, VecPt, ReVecPt);
//45도 배관 시작점 위치가 메인관의 오른쪽인가
bool b_Vir_MainRight = Util.isRightPoint(spCon.Origin, VecPt, ReVecPt);
//다를 경우 Mirror로 돌리기
if (b_Vir_BranchConRIght != b_Vir_MainRight)
{
Plane plane = Plane.CreateByNormalAndOrigin(mainVec, FamLoc);
ElementTransformUtils.MirrorElements(doc, new List<ElementId> { yTeeFam.Id }, plane, false);
doc.Regenerate();
}
//45도 파이프 시작-끝 방향 벡터와 삽입된 Y관Tee의 45도커넥터-Y관삽입점 방향 벡터가
//같을 때까지 반복하여90도로 돌리기
while (true)
{
ElementTransformUtils.RotateElement(doc, yTeeFam.Id, mainLine, Util.DTR(90));
doc.Regenerate();
//삽입된 Y관Tee의 45도커넥터-Y관삽입점 방향 벡터
XYZ Vec_45TeeCon = ((yTeeFam.Location as LocationPoint).Point - branchConLst.First().Origin).Normalize();
if (Vec_45Pipe.IsAlmostEqualTo(Vec_45TeeCon)) break;
}//while end
List<ElementId> divLst = new List<ElementId>();
//메인관 자르기
divLst = Util.DivideElement(doc, mainPipe, epCon.Origin);
if (divLst.Count == 0) continue;
foreach (ElementId id in divLst)
{
bool b_contain = dividePipeLst.Contains(id);
if (b_contain == false) dividePipeLst.Add(id);
}
doc.Regenerate();
//trans.Commit();
//자른 파이프로 변경
//자른 파이프 리스트에서 교차점이 있는 파이프 메인파이프로 변경
for (int j = 0; j < dividePipeLst.Count; j++)
{
if (bottomConLst.Count > i + 1)
{
Line divLine = (mainPipe.Location as LocationCurve).Curve as Line;
Connector cons = bottomConLst[i + 1];
IntersectionResult intersection = divLine.Project(cons.Origin);
if (intersection != null)
{
XYZ interpt = intersection.XYZPoint;
List<Connector> divMainConLst = Util.GetElementConnectors(mainPipe);
foreach (Connector mainCon in divMainConLst)
{
if (interpt.IsAlmostEqualTo(mainCon.Origin))
{
mainPipe = doc.GetElement(dividePipeLst[j]) as Pipe;
break;
}
}
}
}
}//for end
//잘린 파이프 리스트에 45도 파이프 추가
List<ElementId> YteeConPipeLst = new List<ElementId>();
YteeConPipeLst.AddRange(divLst);
YteeConPipeLst.Add(LastPipe.Id);
//trans.Start("4");
foreach (ElementId id in YteeConPipeLst)
{
Pipe Connect45Pipe = doc.GetElement(id) as Pipe;
//파이프 커넥터
List<Connector> conLst = Util.GetElementConnectors(Connect45Pipe);
foreach (Connector PipeCon in conLst)
{
//Y관 패밀리 삽입점과 같은 커넥터 찾기
if (PipeCon.Origin.IsAlmostEqualTo(FamLoc))
{
//파이프 역방향 벡터 구하기
XYZ LineDirReVec = null;
Connector pipeSpCon = null, pipeEpCon = null;
Util.GetStartEndConnector(PipeCon.Owner as Pipe, ref pipeSpCon, ref pipeEpCon);
if (PipeCon.Origin.IsAlmostEqualTo(pipeSpCon.Origin))
LineDirReVec = (pipeEpCon.Origin - pipeSpCon.Origin).Normalize();
else
LineDirReVec = (pipeSpCon.Origin - pipeEpCon.Origin).Normalize();
//파이프 역방향으로 100mm 이동한 점
XYZ plr100Pt = Util.Polar(PipeCon.Origin, LineDirReVec, Unit.MMToFeet(100));
//파이프 커넥터 위치 변경
PipeCon.Origin = plr100Pt;
//파이프 커넥터와 가까운 Y관 커넥터 구하기.
double mainDist = double.MaxValue;
Connector closeCon = null;
List<Connector> yFamConLst = Util.GetElementConnectors(yTeeFam);
foreach (Connector yFamCon in yFamConLst)
{
if (yFamCon.Origin.DistanceTo(plr100Pt) < mainDist)
{
mainDist = yFamCon.Origin.DistanceTo(plr100Pt);
closeCon = yFamCon;
}
}
//파이프 커넥터 위치를 Y관 커넥터 위치로 변경
PipeCon.Origin = closeCon.Origin;
//파이프 커넥퇑 Y관 커넥터 연결
PipeCon.ConnectTo(closeCon);
break;
}
}
}//foreach end
//trans.Commit();
}//for end
string error_str = "";
if (idx_Error1 != 0)
error_str += idx_Error1 + "개의 위생기기와 배관사이 연결할 높이가 좁습니다.";
//MessageBox.Show(idx_Error + "개의 45도 파이프가 메인관에 교차하지 않습니다.", "오류");
if (idx_Error2 != 0)
error_str += "\n" + idx_Error2 + "개의 45도 파이프와 메인배관이 교차하지 않습니다.";
if (idx_Error3 != 0)
error_str += "\n" + idx_Error3 + "개의 45도 파이프가 작도할 높이가 좁습니다.";
if (idx_Error4 != 0)
error_str += "\n" + str_Error4;
if (error_str != "")
MessageBox.Show(error_str, "오류");
}
catch (Exception e)
{
//MessageBox.Show("" + e);
}
transGruop.Commit();
}
}
//패밀리 커넥터 중 가장 아래쪽 커넥터 구하기.
Connector GetBottonConnector(FamilyInstance family)
{
List<Connector> FamConLst = Util.GetElementConnectors(family);
double minOffset = double.MaxValue;
Connector bottomCon = null;
foreach (Connector con in FamConLst)
{
if (con.Origin.Z < minOffset)
{
bottomCon = con;
minOffset = con.Origin.Z;
}
}
//Util.Pyosi(doc, bottomCon.Origin, 1);
return bottomCon;
}
/// <summary>
/// 매개 변수 정의를 반환합니다.
/// 주어진 요소와 매개 변수 이름.
/// </summary>
static Definition GetDefinition(Element elem, string parameter_name)
{
IList<Parameter> ps = elem.GetParameters(parameter_name);
int n = ps.Count;
Debug.Assert(1 >= n, "expected maximum one shared parameters" + "named " + parameter_name);
Definition d = (0 == n) ? null
: ps[0].Definition;
return d;
}
public class PipeSelectionFilter : ISelectionFilter
{
public bool AllowElement(Element element)
{
if (element.Category == null) return false;
if (element.Category.Id.IntegerValue == (int)BuiltInCategory.OST_PipeCurves)
{
return true;
}
return false;
}
public bool AllowReference(Reference refer, XYZ point)
{
return false;
}
}
public class HygienicSelectionFilter : ISelectionFilter
{
public bool AllowElement(Element element)
{
if (element.Category == null) return false;
if (element.Category.Id.IntegerValue == (int)BuiltInCategory.OST_PlumbingFixtures)
{
return true;
}
return false;
}
public bool AllowReference(Reference refer, XYZ point)
{
return false;
}
}
}
}