You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在XBim中获取IFC家具与卫生构件的有向边界框或凸包?

使用XBim提取IFC家具/卫生构件的2D轮廓(凸包/非轴对齐边界框)

要实现这个需求,核心流程是:获取构件的3D几何数据 → 投影到目标2D平面 → 计算凸包或非轴对齐边界框。以下是具体步骤和代码实现:

1. 初始化几何上下文

XBim需要先创建几何上下文来解析IFC中的几何表示,提前预生成所有构件的几何数据能提升后续查询效率:

using var store = IfcStore.Open(myFile);
using var context = new Xbim3DModelContext(store);
context.CreateContext(); // 预生成所有构件的几何信息,首次执行耗时较长

2. 遍历构件并处理几何

以家具为例,卫生构件(IIfcSanitaryTerminal)的处理逻辑完全一致,只需替换类型即可:

var furnitures = store.Instances.OfType<IIfcFurniture>();
foreach (var furniture in furnitures)
{
    // 获取构件的三角化3D形状(XBim默认以三角面存储几何)
    var shape = context.ShapeRepository.GetShape(furniture);
    if (shape == null || shape.GeometryType != XbimGeometryType.Triangulation)
        continue;

    // 将3D顶点投影到XY平面(可根据需求调整投影规则,比如取构件所在楼层的Z标高)
    var triangulation = shape.Geometry as XbimTriangulation;
    var projectedPoints = triangulation.Vertices
        .Select(v => new Point2D(v.X, v.Y))
        .ToList();

    // 计算非轴对齐边界框
    var orientedBbox = CalculateOrientedBoundingBox(projectedPoints);
    // 计算凸包
    var convexHullPoints = CalculateConvexHull(projectedPoints);

    // 输出结果示例
    Console.WriteLine($"构件ID: {furniture.GlobalId}");
    Console.WriteLine($"非轴对齐边界框:中心({orientedBbox.Center.X:F2}, {orientedBbox.Center.Y:F2}),半长({orientedBbox.HalfLengths.X:F2}, {orientedBbox.HalfLengths.Y:F2}),旋转角{orientedBbox.Rotation * 180 / Math.PI:F1}°");
    Console.WriteLine($"凸包顶点数:{convexHullPoints.Count}");
}

3. 实现2D计算方法

XBim没有直接提供凸包和非轴对齐边界框的API,需要自己实现计算逻辑:

凸包计算(Andrew算法)

private static List<Point2D> CalculateConvexHull(List<Point2D> points)
{
    if (points.Count <= 1) return points.ToList();

    // 按X坐标排序,X相同则按Y排序
    var sorted = points.OrderBy(p => p.X).ThenBy(p => p.Y).ToList();
    var lower = new List<Point2D>();
    foreach (var p in sorted)
    {
        // 移除凹点
        while (lower.Count >= 2 && CrossProduct(lower[lower.Count-2], lower[lower.Count-1], p) <= 0)
            lower.RemoveAt(lower.Count-1);
        lower.Add(p);
    }

    var upper = new List<Point2D>();
    foreach (var p in sorted.Reverse())
    {
        while (upper.Count >= 2 && CrossProduct(upper[upper.Count-2], upper[upper.Count-1], p) <= 0)
            upper.RemoveAt(upper.Count-1);
        upper.Add(p);
    }

    // 合并并去重首尾重复点
    lower.RemoveAt(lower.Count-1);
    upper.RemoveAt(upper.Count-1);
    lower.AddRange(upper);
    return lower;
}

// 计算叉积:判断三点构成的转向,>0为逆时针,<0为顺时针,=0共线
private static double CrossProduct(Point2D a, Point2D b, Point2D c)
{
    return (b.X - a.X) * (c.Y - a.Y) - (b.Y - a.Y) * (c.X - a.X);
}

非轴对齐边界框计算(PCA主成分分析)

public class OrientedBbox2D
{
    public Point2D Center { get; set; }
    public Vector2D HalfLengths { get; set; }
    public double Rotation { get; set; } // 与X轴的夹角(弧度)
}

private static OrientedBbox2D CalculateOrientedBoundingBox(List<Point2D> points)
{
    if (points.Count == 0) return null;

    // 计算中心点
    double meanX = points.Average(p => p.X);
    double meanY = points.Average(p => p.Y);
    var center = new Point2D(meanX, meanY);

    // 计算协方差矩阵
    double covXX = 0, covXY = 0, covYY = 0;
    foreach (var p in points)
    {
        double dx = p.X - meanX;
        double dy = p.Y - meanY;
        covXX += dx * dx;
        covXY += dx * dy;
        covYY += dy * dy;
    }
    covXX /= points.Count;
    covXY /= points.Count;
    covYY /= points.Count;

    // 求解特征值,获取主方向
    double trace = covXX + covYY;
    double det = covXX * covYY - covXY * covXY;
    double sqrtDet = Math.Sqrt(Math.Abs(trace * trace / 4 - det));
    double lambda1 = trace / 2 + sqrtDet; // 最大特征值对应主方向

    double dirX, dirY;
    if (covXY == 0)
    {
        dirX = lambda1 > covYY ? 1 : 0;
        dirY = lambda1 > covYY ? 0 : 1;
    }
    else
    {
        dirX = lambda1 - covYY;
        dirY = covXY;
        double length = Math.Sqrt(dirX * dirX + dirY * dirY);
        dirX /= length;
        dirY /= length;
    }

    // 将点投影到主方向和垂直方向,计算边界
    var u = new Vector2D(dirX, dirY);
    var v = new Vector2D(-dirY, dirX); // 垂直于主方向的向量

    double minU = double.MaxValue, maxU = double.MinValue;
    double minV = double.MaxValue, maxV = double.MinValue;
    foreach (var p in points)
    {
        double dx = p.X - meanX;
        double dy = p.Y - meanY;
        double projU = dx * u.X + dy * u.Y;
        double projV = dx * v.X + dy * v.Y;
        minU = Math.Min(minU, projU);
        maxU = Math.Max(maxU, projU);
        minV = Math.Min(minV, projV);
        maxV = Math.Max(maxV, projV);
    }

    return new OrientedBbox2D
    {
        Center = center,
        HalfLengths = new Vector2D((maxU - minU)/2, (maxV - minV)/2),
        Rotation = Math.Atan2(dirY, dirX)
    };
}

注意事项

  • 投影平面可自定义:如果构件不在XY平面,可通过furniture.ObjectPlacement获取构件的空间定位,将3D点转换到世界坐标系后再投影到目标平面(比如楼层标高的Z平面)。
  • 复杂构件处理:部分构件可能包含多个几何表示,context.ShapeRepository.GetShape()会返回合并后的整体形状,无需额外处理。
  • 卫生构件适配:只需将OfType<IIfcFurniture>()替换为OfType<IIfcSanitaryTerminal>()即可复用所有逻辑。

内容的提问来源于stack exchange,提问作者emilaz

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.24 22:37:07