一、前言 應該是第二次寫關於Revit API的博文了。雖然在BIM企業中工作,從事桌面BIM軟體開發,但是我是不怎麼喜歡寫Revit API相關的代碼。平時更多的是在寫界面展示,架構維護,授權驗證這塊。為什麼不喜歡Revit API呢?其實Autodesk封裝的這套API是不錯的(我在之後的工作中 ...
一、前言
應該是第二次寫關於Revit API的博文了。雖然在BIM企業中工作,從事桌面BIM軟體開發,但是我是不怎麼喜歡寫Revit API相關的代碼。平時更多的是在寫界面展示,架構維護,授權驗證這塊。為什麼不喜歡Revit API呢?其實Autodesk封裝的這套API是不錯的(我在之後的工作中用起來挺舒服的),可能還是人比較懶吧,老查英文的API手冊有點煩,而且這手冊界面讓我想起了上學時幫導師寫ObjectARX的痛苦經歷。。。
吐槽完之後,開始上乾貨。為什麼需要去判斷梁構件是否有支座?原因有以下幾個點:
1. 提醒BIM建模設計師其設計的梁構件是否正確,說白點就是:你丫畫對了沒?!
a. 如果有支座,支座與梁的位置是否符合圖紙;
b. 如果沒支座,為什麼?是畫錯了(畫成虛接觸)還是本身就沒有支座;
2. 如果梁有支座,OK!我根據平法規則自動分析節點並自動生成鋼筋,完美;(這是後話,此篇不做節點分析與鋼筋生成)
開發這個功能還是想幫助建模人員去檢查自己畫的模型。中國之大,BIM建模人員參差不齊,一個人就一種畫法。
本篇會以圖文代碼結合的方式敘述,保證初來認識Revit的朋友也能看得懂,也希望高手能提提改進建議。
二、正文
Revit 版本:2016,
主函數:IsBeamHasSeat(Element element),
作用:判斷梁構件是否有支座,適用於直梁與弧形梁。
思路:因為梁有支座的話肯定要有2個構件作為其支座,不然就翹腳了。counter就是計數器,如果到2了,就不用執行了,返回true即可。
說明:
1. 相交構件的函數GetJointElements是自己封裝的,各位可根據實際自己編寫,不暴露了。作用就是獲取該梁周圍一定範圍內的所有構件;
2. IsStructrualColumn(),IsStructrualBeam()也是自己封裝的,比較簡單,不暴露了。作用就是要求作為梁支座的柱與梁是結構型構件,牆不用。
3. IsCoulmnBeSeatForBeam(),IsWallBeSeatForBeam(),IsBeamBeSeatForBeam() 這三個函數是核心,下麵會慢慢展開講。
public static bool IsBeamHasSeat(Element element)
{ int counter = 0; //獲取相交構件 var jointElements = BaseGeomUtils.GetJointElements(element); if (jointElements != null && jointElements.Any()) { //檢查柱的 foreach (var column in jointElements) { if (column.IsStructuralColumn() && IsCoulmnBeSeatForBeam(element, column)) { counter++; } if (counter == 2) { return true; } } //檢查牆的 foreach (var wall in jointElements) { if (IsWallBeSeatForBeam(element, wall)) { counter++; } if (counter == 2) { return true; } } //檢查梁的 foreach (var beam in jointElements) { if (beam.IsStructuralBeam() && IsBeamBeSeatForBeam(element, beam)) { counter++; } if (counter == 2) { return true; } } } return false; }
A. 判斷柱構件是否為當前梁的支座
這時候要上圖了,方便理解。
第1個大條件:
如果不符合這個條件,說明梁與柱可能是側面相交的關係:
private static bool IsCoulmnBeSeatForBeam(Element currentBeam, Element jointColumn) { try { var minArea = 0.1; var columnBottomFace = FaceUtils.GetBottomFace(jointColumn, minArea); var beamBottomFace = FaceUtils.GetBottomFace(currentBeam, minArea); //柱的下底面低於梁的下底面 bool isLower = columnBottomFace.Origin.Z < beamBottomFace.Origin.Z; //取Location和LocationCurve var columnLocation = jointColumn.Location as LocationPoint; var beamLocationCurve = currentBeam.Location as LocationCurve; if (columnLocation != null && beamLocationCurve != null) { //1.Column的Location在Beam的Location下麵 //2.Column的LocationPoint能映射到Beam的下底面 //3.Beam中有點可以投影到Column的下底面 if (beamBottomFace.Project(columnLocation.Point) != null || beamLocationCurve.Curve.Tessellate().Any(x => columnBottomFace.Project(x) != null && isLower)) { return true; } //側面關係 else { var bc = beamLocationCurve.Curve; //獲取構件側面(除去Z方向上下兩個面) var columnFaces = FaceUtils.GetSideFaces(jointColumn); var beamFaces = FaceUtils.GetSideFaces(currentBeam); var line = bc as Line; if (line != null) { var beamLine = line.Direction; //梁兩端的面 var terminalFaces = beamFaces.Where(x => (x as PlanarFace).SafelyFaceNormal().IsAlmostEqualTo(beamLine) || (x as PlanarFace).SafelyFaceNormal().IsAlmostEqualTo(-beamLine)); //判斷梁的兩端面與支座梁的側面有沒有相交 if (columnFaces.Any(fc => terminalFaces.Any(tf => tf.Intersect(fc) == FaceIntersectionFaceResult.Intersecting))) { return true; } //沒有相交則繼續 //找到梁與柱相交面 var matchedItem = columnFaces.Where( x => (x as PlanarFace).SafelyFaceNormal().IsAlmostEqualTo(-beamLine) || (x as PlanarFace).SafelyFaceNormal().IsAlmostEqualTo(beamLine)); var sp = line.GetEndPoint(0); var ep = line.GetEndPoint(1); //梁的端點能否投影到柱的相交面上 if (matchedItem.Any()) { var canProject = matchedItem.Any(face => face.Project(sp) != null || face.Project(ep) != null); if (canProject && isLower) { return true; } } } else if (bc is Arc) { var sp = bc.GetEndPoint(0); var ep = bc.GetEndPoint(1); var points = bc.Tessellate().Where(x => !x.IsAlmostEqualTo(sp) && !x.IsAlmostEqualTo(ep)); var closeSp = points.FirstOrDefault(); var closeEp = points.LastOrDefault(); var tangentSp = closeSp - sp; var tangentEp = closeEp - ep; //找到梁與柱相交面 var matchedItem = columnFaces.Where(x => (x as PlanarFace).SafelyFaceNormal().AngleTo(-tangentSp) < UnitConverter.AngleToRad(30) || (x as PlanarFace).SafelyFaceNormal().AngleTo(tangentSp) < UnitConverter.AngleToRad(30) || (x as PlanarFace).SafelyFaceNormal().AngleTo(-tangentEp) < UnitConverter.AngleToRad(30) || (x as PlanarFace).SafelyFaceNormal().AngleTo(tangentEp) < UnitConverter.AngleToRad(30)); //梁的端點能否投影到柱的相交面上 if (matchedItem.Any() && isLower) { var isInsect = columnFaces.Any(fc => beamFaces.Any(x => x.Intersect(fc) == FaceIntersectionFaceResult.Intersecting)); var canProject = matchedItem.Any(face => face.Project(sp) != null || face.Project(ep) != null); if ((isInsect || canProject) && isLower) { return true; } } } } } } catch (Exception) { return false; } return false; }
B. 判斷牆構件是否為當前梁的支座
牆的判斷與柱類似,但是牆沒有LocationPoint,但有LocationCurve,所以是這樣的:
private static bool IsWallBeSeatForBeam(Element currentBeam, Element jointWall) { try { if (jointWall is Wall) { var currentWall = jointWall as Wall; var minArea = 0.1; var wallBottomFace = FaceUtils.GetBottomFace(currentWall, minArea); var beamBottomFace = FaceUtils.GetBottomFace(currentBeam, minArea); //牆的下底面低於梁的下底面 bool isLower = wallBottomFace.Origin.Z < beamBottomFace.Origin.Z; //取LocationCurve var wallLocationCurve = currentWall.Location as LocationCurve; var beamLocationCurve = currentBeam.Location as LocationCurve; if (wallLocationCurve != null && beamLocationCurve != null) { //1.Wall的Location在Beam的Location下麵 //2.Wall中有點可以投影到Beam的下底面 //3.Beam中有點可以投影到Wall的下底面 if (wallLocationCurve.Curve.Tessellate().Any(pt => beamBottomFace.Project(pt) != null || beamLocationCurve.Curve.Tessellate().Any(x => wallBottomFace.Project(x) != null && isLower))) { return true; } //側面關係 else { var bc = beamLocationCurve.Curve; var wallFaces = FaceUtils.GetSideFaces(currentWall); var beamFaces = FaceUtils.GetSideFaces(currentBeam); var line = bc as Line; if (line != null) { var beamLine = line.Direction; //梁兩端的面 var terminalFaces = beamFaces.Where(x => (x as PlanarFace).SafelyFaceNormal().IsAlmostEqualTo(beamLine) || (x as PlanarFace).SafelyFaceNormal().IsAlmostEqualTo(-beamLine)); //判斷梁的兩端面與支座梁的側面有沒有相交 if (wallFaces.Any(fc => terminalFaces.Any(tf => tf.Intersect(fc) == FaceIntersectionFaceResult.Intersecting))) { return true; } //沒有相交則繼續 //找到梁與牆相交面 var matchedItem = wallFaces.Where(x => (x as PlanarFace).SafelyFaceNormal().IsAlmostEqualTo(-beamLine) || (x as PlanarFace).SafelyFaceNormal().IsAlmostEqualTo(beamLine)); var sp = line.GetEndPoint(0); var ep = line.GetEndPoint(1); //梁的端點能否投影到牆的相交面上 if (matchedItem.Any()) { var canProject = matchedItem.Any(face => face.Project(sp) != null || face.Project(ep) != null); if (canProject && isLower) { return true; } } } else if (bc is Arc) { var sp = bc.GetEndPoint(0); var ep = bc.GetEndPoint(1); var points = bc.Tessellate().Where(x => !x.IsAlmostEqualTo(sp) && !x.IsAlmostEqualTo(ep)); var closeSp = points.FirstOrDefault(); var closeEp = points.LastOrDefault(); var tangentSp = closeSp - sp; var tangentEp = closeEp - ep; //找到梁與牆相交面 var matchedItem = wallFaces.Where(x => (x as PlanarFace).SafelyFaceNormal().AngleTo(-tangentSp) < UnitConverter.AngleToRad(30) || (x as PlanarFace).SafelyFaceNormal().AngleTo(tangentSp) < UnitConverter.AngleToRad(30) || (x as PlanarFace).SafelyFaceNormal().AngleTo(-tangentEp) < UnitConverter.AngleToRad(30) || (x as PlanarFace).SafelyFaceNormal().AngleTo(tangentEp) < UnitConverter.AngleToRad(30)); //梁的端點能否投影到牆的相交面上 if (matchedItem.Any()) { var isInsect = wallFaces.Any(fc => beamFaces.Any(x => x.Intersect(fc) == FaceIntersectionFaceResult.Intersecting)); var canProject = matchedItem.Any(face => face.Project(sp) != null || face.Project(ep) != null); if ((isInsect || canProject) && isLower) { return true; } } } } } } } catch (Exception) { return false; } return false; }
C. 判斷梁構件是否為當前梁的支座,這塊是最最最煩的。
為什麼?因為:
下麵是梁作為另一個梁的支座的完整思路:
如果兩個構件不是上下關係,那就要檢查側面相交關係。
我這裡使用了若幹個條件:conditionOne && (conditionTwo || conditionThree || conditionSp)
private static bool IsBeamBeSeatForBeam(Element currentBeam, Element jointBeam) { try { var beamLocationCurve = currentBeam.Location as LocationCurve; var seatBeamLocationCurve = jointBeam.Location as LocationCurve; var beamFaces = FaceUtils.GetSideFaces(currentBeam); var seatBeamFaces = FaceUtils.GetSideFaces(jointBeam); var seatBeamBottomFace = FaceUtils.GetBottomFace(jointBeam); if (beamLocationCurve != null && seatBeamLocationCurve != null) { var bc = beamLocationCurve.Curve; if (bc is Line) { var beamLine = (bc as Line).Direction; var sp = bc.GetEndPoint(0); var ep = bc.GetEndPoint(1); var matchedItem = seatBeamFaces.Where(x => (x as PlanarFace).SafelyFaceNormal().IsAlmostEqualTo(-beamLine) || (x as PlanarFace).SafelyFaceNormal().IsAlmostEqualTo(beamLine) || (x as PlanarFace).SafelyFaceNormal().AngleTo(beamLine) < UnitConverter.AngleToRad(30) || (x as PlanarFace).SafelyFaceNormal().AngleTo(-beamLine) < UnitConverter.AngleToRad(30)); if (matchedItem.Any()) { //支座梁在主體梁的下方 var canBeamCurvePointProjectToSeatBeamBottomFace = bc.Tessellate().Any(x => seatBeamBottomFace.Project(x) != null); if (canBeamCurvePointProjectToSeatBeamBottomFace) { return true; } } //支座梁在主體梁的側面 //獲取梁的兩端的面 var terminalFaces = beamFaces.Where(x => (x as PlanarFace).SafelyFaceNormal().IsAlmostEqualTo(beamLine) || (x as PlanarFace).SafelyFaceNormal().IsAlmostEqualTo(-beamLine)); if (terminalFaces.Count() != 2) { terminalFaces = beamFaces; } //判斷梁的兩端面與支座梁的側面有沒有相交 var result = BeamHasSeatWithBeam(terminalFaces, seatBeamFaces, beamLine, matchedItem, sp, ep); if (result) { return true; } } else if (bc is Arc) { var sp = bc.GetEndPoint(0); var ep = bc.GetEndPoint(1); var points = bc.Tessellate().Where(x => !x.IsAlmostEqualTo(sp) && !x.IsAlmostEqualTo(ep)); var closeSp = points.FirstOrDefault(); var closeEp = points.LastOrDefault(); var tangentSp = closeSp - sp; var tangentEp = closeEp - ep; var matchedItem = seatBeamFaces.Where(x => (x as PlanarFace).SafelyFaceNormal().AngleTo(-tangentSp) < UnitConverter.AngleToRad(30) || (x as PlanarFace).SafelyFaceNormal().AngleTo(tangentSp) < UnitConverter.AngleToRad(30) || (x as PlanarFace).SafelyFaceNormal().AngleTo(-tangentEp) < UnitConverter.AngleToRad(30) || (x as PlanarFace).SafelyFaceNormal().AngleTo(tangentEp) < UnitConverter.AngleToRad(30)); if (matchedItem.Any()) { var isInsect = seatBeamFaces.Any(fc => beamFaces.Any(x => x.Intersect(fc) == FaceIntersectionFaceResult.Intersecting)); var canProject = matchedItem.Any(face => face.Project(sp) != null || face.Project(ep) != null); if (isInsect || canProject) { return true; } } } } } catch (Exception) { return false; } return false; }
private static bool BeamHasSeatWithBeam(IEnumerable<Face> terminalFaces, IEnumerable<Face> seatBeamFaces, XYZ beamLine,
IEnumerable<Face> seatBeamSpecialFaces, XYZ startpoint, XYZ endpoint) { //梁任意端點能投影到支座梁的對迎面 var conditionSp = seatBeamSpecialFaces.Any() && seatBeamSpecialFaces.Any(x => x.Project(startpoint) != null || x.Project(endpoint) != null); foreach (var tf in terminalFaces) { foreach (var fc in seatBeamFaces) { //支座梁的Face生成Solid var seatBeamCl = (fc as PlanarFace).GetEdgesAsCurveLoops().ToList(); Solid seatBeamTempSolid = GeometryCreationUtilities.CreateExtrusionGeometry(seatBeamCl, fc.ComputeNormal(new UV().Negate()), UnitUtils.ConvertToInternalUnits(0.5, DisplayUnitType.DUT_MILLIMETERS)); var seatBeamDestFaces = (from object f in seatBeamTempSolid.Faces where (f as PlanarFace).SafelyFaceNormal().IsAlmostEqualTo(beamLine) ||
(f as PlanarFace).SafelyFaceNormal().IsAlmostEqualTo(-beamLine) ||
(f as PlanarFace).Project(startpoint) != null ||
(f as PlanarFace).Project(endpoint) != null select f as PlanarFace).ToList(); if (!seatBeamDestFaces.Any()) { seatBeamDestFaces = (from object f in seatBeamTempSolid.Faces select f as PlanarFace).ToList(); } //梁的Face生成Solid var beamCL = (tf as PlanarFace).GetEdgesAsCurveLoops().ToList(); Solid beamTempSolid = GeometryCreationUtilities.CreateExtrusionGeometry(beamCL, tf.ComputeNormal(new UV().Negate()), UnitUtils.ConvertToInternalUnits(0.5, DisplayUnitType.DUT_MILLIMETERS)); var beamDestFaces = (from object f in beamTempSolid.Faces select f as PlanarFace).ToList(); //梁與支座梁有相交 var conditionOne = seatBeamDestFaces.Any(x=> tf.Intersect(x) == FaceIntersectionFaceResult.Intersecting || x.Project(startpoint) != null || x.Project(endpoint) != null) || seatBeamDestFaces.Any(x=> beamDestFaces.Any(y=>y.Intersect(x) == FaceIntersectionFaceResult.Intersecting)); var fc1 = (tf as PlanarFace).SafelyFaceNormal(); var conditionTwo = seatBeamDestFaces.Any(x=> fc1.IsAlmostEqualTo(x.SafelyFaceNormal()) || fc1.IsAlmostEqualTo(-x.SafelyFaceNormal())); var conditionThree = seatBeamDestFaces.Any(x => fc1.AngleTo(x.SafelyFaceNormal()) < UnitConverter.AngleToRad(30) || fc1.AngleTo(x.SafelyFaceNormal()) < UnitConverter.AngleToRad(30)); if (conditionOne && (conditionTwo || conditionThree || conditionSp)) { return true; } } } return false; }
代碼中有一些自己封裝的函數,比如求構件所有底面,求構件所有側面,比較簡單,這裡就不暴露了。本篇主要以思路為主,掌握了思路代碼也就清晰了。
三、結尾
本文只是個拋磚引玉,改造的空間是非常大的,我這裡只判斷true,false。還可以輸出梁的支座構件做進一步分析。
演算法本身還可以改進,在Revit模型中扣減是一個大難題,如何應對複雜扣減情況下的梁支座判斷是非常必要的,特別是判斷梁作為另一個梁的支座時尤其小心,很容易就因為梁上面的一塊板導致梁與梁之間的扣減關係變複雜從而導致IsBeamBeSeatForBeam()適應性變低。最好的解決方式是先檢查構件之間的扣減關係是否正確(扣減部分一直是Revit二次開發的難點),修正之後再調用上述函數。
對演算法有建議或者意見的歡迎在評論區留言!
《原創,轉載請註明來源》
來自:airforce094