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

570 lines
25 KiB
C#

using Autodesk.Revit.UI;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Autodesk.Revit.DB;
using Autodesk.Revit.UI.Selection;
using System.Threading.Tasks;
using KDCS.Utils;
using System.Collections;
using System.Windows.Forms;
using Autodesk.Revit.DB.Plumbing;
using Autodesk.Revit.DB.Mechanical;
using System.Xml.Linq;
using Autodesk.Revit.UI.Events;
using System.IO;
using KMBIM.Revit.Tools;
using KMBIM.Revit.Tools.Cmd.ColorSort;
using System.Resources;
using KMBIM.Revit.Tools.Cmd.PipeMatchInterval;
using KMBIM.Revit.Tools.Cmd.DuctOffsetConnect;
using Autodesk.Revit.DB.Structure;
using Autodesk.Revit.DB.Events;
namespace KMBIM
{
/// <summary>
/// 1. 두 덕트를 선택한다.
/// 2. 페밀리를 삽입한다.
/// 3. 파라미터를 일치 시킨다.
/// 4. 1에서 선택한 지점에서 제일 가까운 커넥터 두 개 중 Z값 높은 것에 페밀리가 맞도록 페밀리 이동
/// 5. 가까운 덕트의 커넥터와 페밀리 커넥터를 Fitting
/// </summary>
[Autodesk.Revit.Attributes.Transaction(Autodesk.Revit.Attributes.TransactionMode.Manual)]
public class DuctOffsetConnect : IExternalCommand
{
UIApplication uiapp;
UIDocument uidoc;
static public Autodesk.Revit.DB.Document doc = null;
Autodesk.Revit.ApplicationServices.Application app = null;
public Autodesk.Revit.UI.Result Execute(ExternalCommandData commandData, ref string message, ElementSet elements)
{
try
{
if (!WorkMain.GetInstance().IsValid) return Autodesk.Revit.UI.Result.Succeeded;
while (true)
{
// Verify active document
if (null == commandData.Application.ActiveUIDocument.Document)
{
message = "현재 활성화된 뷰가 없습니다.";
return Result.Failed;
}
uiapp = commandData.Application;
uidoc = uiapp.ActiveUIDocument;
app = uiapp.Application;
doc = uidoc.Document;
Reference r1 = uidoc.Selection.PickObject(ObjectType.Element, "첫번째 덕트 지정");
Reference r2 = uidoc.Selection.PickObject(ObjectType.Element, "두번째 덕트 지정");
XYZ selPt1 = r1.GlobalPoint;
XYZ selPt2 = r2.GlobalPoint;
if (r1 == null || r2 == null) return Result.Failed;
Duct duct1 = doc.GetElement(r1.ElementId) as Duct;
Duct duct2 = doc.GetElement(r2.ElementId) as Duct;
//덕트 간격 띄우기 값으로 정렬
List<Duct> duList = new List<Duct> { duct1, duct2 };
duList.Sort((a, b) => a.get_Parameter(BuiltInParameter.RBS_OFFSET_PARAM).AsDouble().
CompareTo(b.get_Parameter(BuiltInParameter.RBS_OFFSET_PARAM).AsDouble()));
Duct dt1 = duList[1];//상대적으로 높이 있는 배관
Duct dt2 = duList[0];//낮은 배관
if (dt1 == null || dt2 == null)
{
MessageBox.Show("덕트를 선택하십시오.", "오류");
return Result.Failed;
}
double duct1Height = dt1.get_Parameter(BuiltInParameter.RBS_CURVE_HEIGHT_PARAM).AsDouble();//높이
double duct1Width = dt2.get_Parameter(BuiltInParameter.RBS_CURVE_WIDTH_PARAM).AsDouble();//폭
//선택한 지점과 가장 가까운 커넥터 찾기
ConnectorSet cs1 = dt1.ConnectorManager.Connectors;
ConnectorSet cs2 = dt2.ConnectorManager.Connectors;
Connector insertCon1 = GetConnectorClosestTo(cs1, selPt1);
Connector insertCon2 = GetConnectorClosestTo(cs2, selPt2);
List<Connector> insertConList = new List<Connector> { insertCon1, insertCon2 };
insertConList.Sort((a, b) => a.Origin.Z.CompareTo(b.Origin.Z));
XYZ pt1 = new XYZ();
XYZ pt2 = new XYZ();
//무조건 Z값 큰게 pt1
pt1 = insertConList[1].Origin;//Z값 큰 포인트
pt2 = insertConList[0].Origin;//z값 작은 포인트
XYZ pt1pt2Vec = (pt2 - pt1).Normalize();
AngleSelect ags = new AngleSelect();
ags.ShowDialog();
double angleValue = Util.DTR(ags.inputValue);//유저에게 입력받은 각도 값
if (ags.isConfirmed == false) return Result.Cancelled;
using (Transaction tr = new Transaction(doc))
{
tr.Start("Transaction Start");
FamilySymbol fs = LoadFamily("T_Rectangular_UPDOWN");//페밀리 로드
if (fs.IsActive == false)
fs.Activate();
FamilyInstance fi = null;
Connector con1 = null, con2 = null, con3 = null, con4 = null;
Util.GetStartEndConnector(dt1, ref con1, ref con2);
Util.GetStartEndConnector(dt2, ref con3, ref con4);
//Util.Pyosi(doc, con1.Origin, 0);
//Util.Pyosi(doc, con2.Origin, 0);
List<Connector> conLst = new List<Connector>();
conLst.Add(con1); conLst.Add(con2); conLst.Add(con3); conLst.Add(con4);
Connector MinCon1 = null, MinCon2 = null;
double minDist = double.MaxValue;
foreach (Connector DirCon in conLst)
{
foreach (Connector con in conLst)
{
if (Util.GetNextElementConnector(con) != null) continue;
if (con.Origin.IsAlmostEqualTo(DirCon.Origin)) continue;
//길이 최솟값 구하기
if (con.Origin.DistanceTo(DirCon.Origin) < minDist)
{
minDist = con.Origin.DistanceTo(DirCon.Origin);
MinCon1 = con;
MinCon2 = DirCon;
}
}
}
//삽입점을 구하기 위해 Z값 높은 커넥터 구하기
Connector HgtCon = null;
if (MinCon1.Origin.Z > MinCon2.Origin.Z)
HgtCon = MinCon1;
else
HgtCon = MinCon2;
//Util.Pyosi(doc, HgtCon.Origin, 0);
List<Connector> fiConList = new List<Connector>();
XYZ HgtVec = null;
if (Util.IsEqual(con1.Origin.Z, con2.Origin.Z, Unit.MMToFeet(5)))//삽입해야 할 덕트들이 가로로 그려진 경우
{
Connector otherHgtCon = Util.GetOtherConnector(HgtCon);
HgtVec = (HgtCon.Origin - otherHgtCon.Origin).Normalize();//Z값 높은 덕트의 벡터 구하기
//페밀리 삽입
fi = doc.Create.NewFamilyInstance(HgtCon.Origin, fs, HgtVec, HgtCon.Owner, Autodesk.Revit.DB.Structure.StructuralType.NonStructural);
doc.Regenerate();
double pt1Mpt2 = pt1.Z - pt2.Z;//패밀리 높이값 지정을 위해 높이 구하기
//Paramter 조정
SyncDuctParameters(fi, duct1Height, duct1Width, angleValue, pt1Mpt2);
doc.Regenerate();
fiConList = Util.GetElementConnectors(fi);//페밀리 커넥터
//Z값 기준으로 정리. Z값 작은 값부터 나열(오름차순)
fiConList.Sort((a, b) => a.Origin.Z.CompareTo(b.Origin.Z));
XYZ famPt1 = fiConList[1].Origin;//z값 큰 포인트
XYZ famPt2 = fiConList[0].Origin;//Z값 작은 포인트
double dist = pt2.DistanceTo(famPt2);
XYZ vec = (pt2 - famPt2).Normalize() * dist;
//덕트 이동
ElementTransformUtils.MoveElement(doc, fi.Id, vec);
insertConList[1].Origin = fiConList[1].Origin;
fiConList[1].ConnectTo(insertConList[1]);
insertConList[0].Origin = fiConList[0].Origin;
fiConList[0].ConnectTo(insertConList[0]);
}
else//삽입해야 할 덕트들이 수직으로 그려진 경우
{
MessageBox.Show("S 덕트 기능은 수직 덕트에는 적용되지 않습니다.", "오류");
return Result.Cancelled;
//fi = doc.Create.NewFamilyInstance(HgtCon.Origin, fs, StructuralType.NonStructural);
//doc.Regenerate();
//
//fiConList = Util.GetElementConnectors(fi);//페밀리 커넥터
//
////Z값 기준으로 정리. Z값 작은 값부터 나열(오름차순)
//fiConList.Sort((a, b) => a.Origin.Z.CompareTo(b.Origin.Z));
//
//XYZ famPt1 = fiConList[1].Origin;//z값 큰 포인트
//XYZ famPt2 = fiConList[0].Origin;//Z값 작은 포인트
//XYZ famMidPt = Util.Midpoint(famPt1, famPt2);
//double HeightForVertical = Math.Abs(pt1.Z - pt2.Z);
//
////Paramter 조정
//SyncDuctParameters(fi, duct1Height, duct1Width, angleValue, HeightForVertical);
//doc.Regenerate();
//
////pt2에서 Z축 방향으로 연장한 직선을 만들어서 pt1을 투영시킨다.
////투영된 점(newPt)과 pt1 벡터를 구해서 이를 기준으로 pt1->famPt 벡터를 만들어서
////두 벡터의 각도를 구하고, 이 각도만큼 페밀리를 회전시켜 방향을 맞춘다.
//XYZ pt2ZEx = new XYZ(pt2.X, pt2.Y, pt2.Z + 100);
//Line zExtension = Line.CreateBound(pt2, pt2ZEx);
//IntersectionResult ir = zExtension.Project(pt1);
//XYZ newPt = ir.XYZPoint;
//
//XYZ pt1ToNewPtVec = (newPt - pt1).Normalize();
//XYZ pt1TofamPt1Vec = (famPt2 - pt1).Normalize();
//
//double an = Util.RTD(pt1TofamPt1Vec.AngleTo(pt1ToNewPtVec));
//
//RotateFamilyZAxis2(fi, famPt1, -an);
//doc.Regenerate();
//
////페밀리 위치가 바뀌었기 때문에 패밀리 커넥터 좌표 세롭게 조사
//List<Connector> newConList = Util.GetElementConnectors(fi);
//newConList.Sort((a, b) => a.Origin.Z.CompareTo(b.Origin.Z));
//famPt1 = newConList[1].Origin;
//famPt2 = newConList[0].Origin;
//
//Line famAxis = Line.CreateBound(famPt1, famPt2);
////ElementTransformUtils.RotateElement(doc, fi.Id, famAxis, Util.DTR(180));
//doc.Regenerate();
//
////페밀리 위치가 바뀌었기 때문에 패밀리 커넥터 좌표 세롭게 조사
//List<Connector> newConList2 = Util.GetElementConnectors(fi);
//newConList2.Sort((a, b) => a.Origin.Z.CompareTo(b.Origin.Z));
//famPt1 = newConList[1].Origin;
//famPt2 = newConList[0].Origin;
//
//double mVal = famPt1.Z - pt1.Z;
//XYZ newLoc = new XYZ(famPt1.X, famPt1.Y, famPt1.Z - mVal);
//MoveFamilyInstance(doc, fi, newLoc);
//doc.Regenerate();
//
//RotateFamilyZAxis(fi, famPt1, famPt2, 180);
//
//insertConList[1].Origin = fiConList[1].Origin;
//fiConList[1].ConnectTo(insertConList[1]);
//insertConList[0].Origin = fiConList[0].Origin;
//fiConList[0].ConnectTo(insertConList[0]);
//Connector m_dt1Con1 = null, m_dt1Con2 = null, m_dt2Con1 = null, m_dt2Con2 = null;
//Util.GetStartEndConnector(dt1, ref m_dt1Con1, ref m_dt1Con2);
//Util.GetStartEndConnector(dt2, ref m_dt2Con1, ref m_dt2Con2);
//
//List<Connector> m_dtConLst1 = Util.GetElementConnectors(dt1);
//List<Connector> m_dtConLst2 = Util.GetElementConnectors(dt2);
//
//Connector closeCon1 = null, closeCon2 = null;
//double min2C2 = double.MaxValue;
//foreach(Connector dtcon1 in m_dtConLst1)
//{
// foreach (Connector dtcon2 in m_dtConLst2)
// {
// if (dtcon1.Origin.DistanceTo(dtcon2.Origin) < min2C2)
// {
// min2C2 = dtcon1.Origin.DistanceTo(dtcon2.Origin);
// closeCon1 = dtcon1;
// closeCon2 = dtcon2;
// }
// }
//}
//
//Util.Pyosi(doc, closeCon1.Origin);
//Util.Pyosi(doc, closeCon2.Origin);
////두 덕트의 가까운 커넥터의 X축이 같은지
//if(Math.Abs(closeCon1.Origin.X - closeCon2.Origin.X) < 0.001)
//{
// fi = doc.Create.NewFamilyInstance(closeCon1.Origin, fs, StructuralType.NonStructural);
//}//두 덕트의 가까운 커넥터의 Y축이 같은지
//else if (Math.Abs(closeCon1.Origin.Y - closeCon2.Origin.Y) < 0.001)
//{
//
//}
}
doc.Regenerate();
tr.Commit();
}
}
}
catch (Exception e)
{
//MessageBox.Show(e.Message);
}
return Result.Succeeded;
}
public Connector GetConnectorClosestTo(ConnectorSet connectors, XYZ p)
{
Connector targetConnector = null;
double minDist = double.MaxValue;
foreach (Connector c in connectors)
{
double d = c.Origin.DistanceTo(p);
if (d < minDist)
{
targetConnector = c;
minDist = d;
}
}
return targetConnector;
}
public FamilySymbol LoadFamily(string familyName)
{
FamilySymbol fs = null;
//태그 패밀리 불러오기
FilteredElementCollector symbolcollector = new FilteredElementCollector(doc);
ICollection<Element> symbolcollection = symbolcollector.OfCategory(BuiltInCategory.OST_DuctFitting).ToElements();
foreach (Element element in symbolcollection)
{
FamilySymbol symbol = element as FamilySymbol;
if (symbol != null)
{
if (symbol.FamilyName == familyName)
{
fs = symbol;
break;
}
}
}
//패밀리가 도면에 로드되어 있지 않을 경우 로드 , 경로 버전별로 나눔
if (fs == null)
{
string versionNumber = doc.Application.VersionNumber.ToString();
string tmp = Util.GetKMBIMLibraryFolder("Libraries\\DuctOffsetConnect");
string fileName = null;
if (familyName == "T_Rectangular_UPDOWN")
{
fileName = tmp + "\\" + versionNumber + "\\T_Rectangular_UPDOWN.rfa";
}
Family fam = null;
//doc.LoadFamilySymbol(fileName, familyName, out fs);
doc.LoadFamily(fileName, out fam);
if (fam != null)
{
ISet<ElementId> fam_ids = fam.GetFamilySymbolIds();
List<string> fam_sym_names = new List<string>();
if (fam_ids.Count() == 0) return null;
fs = (FamilySymbol)doc.GetElement(fam_ids.ElementAt(0));
}
}
return fs;
}
// 패밀리 위치 이동(패밀리 인스턴스는 Transform 이용해야 해서 만듦)
public static void MoveFamilyInstance(Document doc, FamilyInstance family, XYZ MovePt)
{
try
{
Transform transform = family.GetTransform();
XYZ Translation = MovePt - transform.Origin;
//패밀리 이동
ElementTransformUtils.MoveElement(doc, family.Id, Translation);
//redraw / 트랜잭션 실행문 안에서 할 것.
doc.Regenerate();
}
catch
{
}
}
public void SyncDuctParameters(FamilyInstance fi, double duct1Height, double duct1Width, double angleValue, double pt1Mpt2)
{
Parameter heightParam = fi.LookupParameter("Height");
Parameter famDuctHeightParam = fi.LookupParameter("Duct Height");
Parameter famDuctWidthParam = fi.LookupParameter("Duct Width");
Parameter famAngleParam = fi.LookupParameter("Angle");
//Paramter 조정
heightParam.Set(pt1Mpt2);
famDuctHeightParam.Set(duct1Height);
famDuctWidthParam.Set(duct1Width);
famAngleParam.Set(angleValue);
}
public void AdjustingAndFittingFamilyWithDuct(XYZ pt1, XYZ famPt1, FamilyInstance fi, Connector insertCon1, Connector insertCon0, Connector familyCon1, Connector familyCon0)
{
double famPt1MPt1 = famPt1.Z - pt1.Z;
XYZ newFamLoc = new XYZ(pt1.X, pt1.Y, pt1.Z - famPt1MPt1);
MoveFamilyInstance(doc, fi, newFamLoc);
doc.Regenerate();
insertCon1.Origin = familyCon1.Origin;
familyCon1.ConnectTo(insertCon1);//pt1과 패밀리 z값 큰 포인트랑 fitting
insertCon0.Origin = familyCon0.Origin;
familyCon0.ConnectTo(insertCon0);
}
/// <summary>
/// 패밀리를 X축 기준으로 회전시킨다. 메서드 안에서 라디안으로 바꿔줌.
/// </summary>
/// <param name="fi"></param>
/// <param name="famPt1"></param>
/// <param name="famPt2"></param>
public void RotateFamilyXAxis(FamilyInstance fi, XYZ famPt1, XYZ famPt2, double angle)
{
XYZ famMidPt = Util.Midpoint(famPt1, famPt2);
XYZ ptForZAxis = new XYZ(famMidPt.X + 100, famMidPt.Y, famMidPt.Z);
Line zAxis = Line.CreateBound(famMidPt, ptForZAxis);
ElementTransformUtils.RotateElement(doc, fi.Id, zAxis, Util.DTR(angle));
}
/// <summary>
/// 패밀리를 Y축 기준으로 회전시킨다. 메서드 안에서 라디안으로 바꿔줌.
/// </summary>
/// <param name="fi"></param>
/// <param name="famPt1"></param>
/// <param name="famPt2"></param>
public void RotateFamilyYAxis(FamilyInstance fi, XYZ famPt1, XYZ famPt2, double angle)
{
XYZ famMidPt = Util.Midpoint(famPt1, famPt2);
XYZ ptForZAxis = new XYZ(famMidPt.X, famMidPt.Y + 100, famMidPt.Z);
Line zAxis = Line.CreateBound(famMidPt, ptForZAxis);
ElementTransformUtils.RotateElement(doc, fi.Id, zAxis, Util.DTR(angle));
}
/// <summary>
/// 패밀리를 Z축 기준으로 회전시킨다. 메서드 안에서 라디안으로 바꿔줌.
/// </summary>
/// <param name="fi"></param>
/// <param name="famPt1"></param>
/// <param name="famPt2"></param>
public void RotateFamilyZAxis(FamilyInstance fi, XYZ famPt1, XYZ famPt2, double angle)
{
XYZ famMidPt = Util.Midpoint(famPt1, famPt2);
XYZ ptForZAxis = new XYZ(famMidPt.X, famMidPt.Y, famMidPt.Z + 100);
Line zAxis = Line.CreateBound(famMidPt, ptForZAxis);
ElementTransformUtils.RotateElement(doc, fi.Id, zAxis, Util.DTR(angle));
}
public void RotateFamilyZAxis2(FamilyInstance fi, XYZ famPt1, double angle)
{
XYZ ptForZAxis = new XYZ(famPt1.X, famPt1.Y, famPt1.Z + 100);
Line zAxis = Line.CreateBound(famPt1, ptForZAxis);
ElementTransformUtils.RotateElement(doc, fi.Id, zAxis, Util.DTR(angle));
}
public XYZ GetDuctVector(Duct dt)
{
List<Connector> cList = Util.GetElementConnectors(dt);
XYZ pt1 = cList[0].Origin;
XYZ pt2 = cList[1].Origin;
XYZ vec = (pt2 - pt1).Normalize();
return vec;
}
/// <summary>
/// 덕트 경사도 구하기
/// </summary>
/// <param name="dt">덕트</param>
/// <returns>덕트 경사도 퍼센테이지</returns>
public double GetDuctSlopePercentage(Duct dt)
{
double slope = dt.get_Parameter(BuiltInParameter.RBS_DUCT_SLOPE).AsDouble();
double slopePer = slope * 100;//경사도
return slopePer;
}
public void ModifyingDuctForVerticalDuct(FamilyInstance fi, XYZ famMidPt, XYZ famPt1, XYZ famPt2)
{
RotateFamilyZAxis(fi, famPt1, famPt2, 180);
XYZ extentionToY = new XYZ(famMidPt.X, famMidPt.Y + 100, famMidPt.Z);
Line axis = Line.CreateBound(famMidPt, extentionToY);
ElementTransformUtils.RotateElement(doc, fi.Id, axis, Util.DTR(90));
doc.Regenerate();
}
public void ModifyingDuctForVerticalDuctWithoutFilp(FamilyInstance fi, XYZ famMidPt, XYZ famPt1, XYZ famPt2, List<Connector> fiConList, XYZ pt1)
{
//Y축 기준으로 90도 회전
XYZ extentionToY = new XYZ(famMidPt.X, famMidPt.Y + 100, famMidPt.Z);
Line axis = Line.CreateBound(famMidPt, extentionToY);
ElementTransformUtils.RotateElement(doc, fi.Id, axis, Util.DTR(90));
doc.Regenerate();
//아래로 내리기
double famPt1MPt1Z = fiConList[0].Origin.Z - pt1.Z;
XYZ newFamLoc = new XYZ(pt1.X, pt1.Y, pt1.Z - famPt1MPt1Z);
MoveFamilyInstance(doc, fi, newFamLoc);
doc.Regenerate();
//좌로 이동
double famPt2MPt1X = Math.Abs(fiConList[1].Origin.X - pt1.X);
XYZ newFamLoc2 = new XYZ(pt1.X - famPt2MPt1X, pt1.Y, pt1.Z);
MoveFamilyInstance(doc, fi, newFamLoc2);
doc.Regenerate();
}
public double GetMinValue(List<double> doubleList)
{
double minLength = doubleList[0];
for (int i = 0; i < doubleList.Count(); i++)
{
//최솟값 구하기
if (minLength > doubleList[i])
{
minLength = doubleList[i];
}
}
return minLength;
}
}
}