一、前言
应该是第二次写关于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二次开发的难点),修正之后再调用上述函数。
对算法有建议或者意见的欢迎在评论区留言!
《原创,转载请注明来源》