在做项目的时候有时候会碰到建筑专业与结构专业同步进行建模的情况,这时因为结构模型尚不完整,建筑的墙建模的高度是标高到标高,而不是实际的板面到板底或梁底。所以需要等到结构模型完整后再重新调整一遍。这个调整的工作虽然比较简单,但重复性比较强,工作量也不少,于是利用二次开发做了一下优化。
具体逻辑是在墙的高度一半的位置选了几个点,根据这些点上下检测楼面及梁面,如果能检测到对应的面,获得距离换算出墙的顶部及底部偏移,然后调整墙的对应参数。
比较麻烦的地方是取点,API中好像没有直接的方法可以获得线上的参数点,需要自己写一下。而且选点的时候也不能选墙路径的两个端点,因为这样会导致判定过于敏感,识别到隔壁图元的面。
示例:
代码:
class ModifyWallOffset : IExternalCommand
{
public Result Execute(ExternalCommandData commandData, ref string message, ElementSet elements)
{
UIDocument uidoc = commandData.Application.ActiveUIDocument;
Document doc = uidoc.Document;
//在三维视图运行
View3D view = doc.ActiveView as View3D;
//选择墙
var elems = uidoc.Selection.PickElementsByRectangle(new AWallSelection(), "框选要修改的墙");
//启用事务
using (Transaction tran = new Transaction(doc, "修正墙"))
{
tran.Start();
//遍历元素
foreach (Wall wall in elems)
{
//如果墙体被锁定,则不对墙体进行修改
if (!wall.Pinned)
{
//参数:顶部偏移、无连接高度、底部偏移
Parameter wallTopOffset = wall.get_Parameter(BuiltInParameter.WALL_TOP_OFFSET);
Parameter wallHeight = wall.get_Parameter(BuiltInParameter.WALL_USER_HEIGHT_PARAM);
Parameter wallBaseOffset = wall.get_Parameter(BuiltInParameter.WALL_BASE_OFFSET);
try
{
//创建射线计算距离
RayTool rt = new RayTool(doc, wall, view);
//初始墙高及初始底偏移
double height = wallHeight.AsDouble();
double baseOffset = wallBaseOffset.AsDouble();
//计算正确的墙高及正确的底偏移
switch (rt.ReturnState)
{
//顶部底部均有数据返回
case 2:
baseOffset = height / 2 + baseOffset - rt.BaseDistance;
height = rt.TopDistance + rt.BaseDistance;
break;
//仅顶部有数据返回
case 1:
height = height / 2 + rt.TopDistance;
break;
//仅底部有数据返回
case -1:
baseOffset = height / 2 + baseOffset - rt.BaseDistance;
height = height + wallBaseOffset.AsDouble() - baseOffset;
break;
}
//调整墙参数
if (wallTopOffset.IsReadOnly)
{
wallHeight.Set(height);
}
else
{
wallTopOffset.Set(height + baseOffset - wallHeight.AsDouble() - wallBaseOffset.AsDouble() + wallTopOffset.AsDouble());
}
wallBaseOffset.Set(baseOffset);
}
catch
{
//error
}
}
}
tran.Commit();
}
return Result.Succeeded;
}
}
//墙过滤器
class AWallSelection : Autodesk.Revit.UI.Selection.ISelectionFilter
{
public bool AllowElement(Element elem)
{
if(elem is Wall)
{
return true;
//Wall wall = elem as Wall;
//if (wall.StructuralUsage == Autodesk.Revit.DB.Structure.StructuralWallUsage.NonBearing)
//{
// return true;
//}
}
return false;
}
public bool AllowReference(Reference reference, XYZ position)
{
return false;
}
}
//使用射线法检测距离
class RayTool
{
public RayTool(Document document,Wall wall,View3D view3D)
{
//射线过滤器
IList filterList = new List();
ElementCategoryFilter filter1 = new ElementCategoryFilter(BuiltInCategory.OST_Floors);
filterList.Add(filter1);
ElementCategoryFilter filter2 = new ElementCategoryFilter(BuiltInCategory.OST_StructuralFraming);
filterList.Add(filter2);
ElementCategoryFilter filter3 = new ElementCategoryFilter(BuiltInCategory.OST_Ceilings );
filterList.Add(filter3);
LogicalOrFilter filter = new LogicalOrFilter(filterList);
//创建射线
ReferenceIntersector referenceIntersector = new ReferenceIntersector(filter, FindReferenceTarget.Face, view3D);
referenceIntersector.FindReferencesInRevitLinks = true;
//到顶部距离
TopDistance = 0;
//到底部距离
BaseDistance = 0;
foreach (XYZ p in GetRayOriginPoints(wall, 5))
{
ReferenceWithContext rwc0 = referenceIntersector.FindNearest(p, new XYZ(0, 0, 1));
if (rwc0 != null)
{
double distance = rwc0.Proximity;
if (distance > TopDistance) TopDistance = distance;
}
ReferenceWithContext rwc1 = referenceIntersector.FindNearest(p, new XYZ(0, 0, -1));
if (rwc1 != null)
{
double distance = rwc1.Proximity;
if (distance > BaseDistance) BaseDistance = distance;
}
}
}
///
/// 获取用于作射线的点列表
///
/// 测量的墙
/// 测量的点数量
///
IList GetRayOriginPoints(Wall wall, int pointNumber)
{
//获取用于计算的线段(墙的水平中线)
Curve wallLocationCurve = (wall.Location as LocationCurve).Curve;
//获取墙的一半高度
double h = wall.get_Parameter(BuiltInParameter.WALL_USER_HEIGHT_PARAM).AsDouble() / 2 + wall.get_Parameter(BuiltInParameter.WALL_BASE_OFFSET).AsDouble();
//获得对应的点
IList originPoints = new List();
double pointParameter = 0.05;
XYZ p = PointAtParameter(pointParameter);
originPoints.Add(new XYZ(p.X, p.Y, p.Z + h));
double pace = (0.95 - pointParameter) / (pointNumber - 1);
for (int i = 0; i < pointNumber - 1; i++)
{
pointParameter += pace;
p = PointAtParameter(pointParameter);
originPoints.Add(new XYZ(p.X, p.Y, p.Z + h));
}
return originPoints;
//根据参数在线上获得点
XYZ PointAtParameter(double parameter)
{
XYZ p1, p2;
//判断线类型,采用不同的方法
Line line = wallLocationCurve as Line;
if (line != null)
{
p1 = line.GetEndPoint(0);
p2 = line.GetEndPoint(1);
return new XYZ(p1.X + parameter * (p2.X - p1.X), p1.Y + parameter * (p2.Y - p1.Y), p1.Z + parameter * (p2.Z - p1.Z));
}
else
{
Arc arc = wallLocationCurve as Arc;
if (arc != null)
{
p1 = arc.GetEndPoint(0);
p2 = arc.GetEndPoint(1);
XYZ c = arc.Center;
double r = arc.Radius;
XYZ v1 = new XYZ(p1.X - c.X, p1.Y - c.Y, p1.Z - c.Z);
XYZ v2 = new XYZ(p2.X - c.X, p2.Y - c.Y, p2.Z - c.Z);
double angle = v1.AngleTo(v2) * parameter;
if (arc.Normal.Z == -1) angle = -angle;
double x1 = c.X + (p1.X - c.X) * Math.Cos(angle) - (p1.Y - c.Y) * Math.Sin(angle);
double y1 = c.Y + (p1.X - c.X) * Math.Sin(angle) + (p1.Y - c.Y) * Math.Cos(angle);
return new XYZ(x1, y1, c.Z);
}
}
return null;
}
}
public int ReturnState
{
get
{
if (TopDistance > 0 && BaseDistance > 0)
{
return 2;
}
else if (TopDistance > 0 && BaseDistance <= 0)
{
return 1;
}
else if (TopDistance <= 0 && BaseDistance > 0)
{
return -1;
}
else
{
return 0;
}
}
}
public double TopDistance { get; }
public double BaseDistance { get; }
}
在项目中实际使用后效果也还可以,面对比较常规的地方都没有太大问题,但在建筑出现沉降或者在一些不同梁高的梁交接的地方会出现与结构梁板相交的问题,这些地方实际是要通过编辑墙轮廓进行修正的,所以还需要考虑检测这些相交的地方,然后对墙轮廓进行自动修正。然后还忽然想到一个斜板斜梁的问题。嗯~坑先挖下,看什么时候能抽出时间回来填吧。