Skip to content
C#
元素与族

元素与族

Revit 的核心是"族"(Family)。族定义了对象的几何形状和元数据,实例化后成为项目中的元素。理解元素和族的创建、修改是关键技能。


Revit 元素体系

Element (所有内容的基类)
  ├── 模型元素(Model Elements)
  │     ├── 系统族(System Family)
  │     │     └── Wall, Floor, Roof, Ceiling, Duct, Pipe...
  │     ├── 可载入族(Loadable Family)
  │     │     └── FamilyInstance → Door, Window, Furniture, Column...
  │     └── 内建族(In-Place Family)
  │           └── 项目中直接建模的独特构件
  ├── 基准元素(Datum Elements)
  │     ├── Level (标高)
  │     └── Grid  (轴网)
  └── 视图元素(View Elements)
        ├── ViewPlan (平面图)
        ├── ViewSection (剖面)
        ├── View3D (三维视图)
        └── ViewSchedule (明细表)

🔬 系统族(如基本墙)在 Revit 内部预定义,不能从外部文件载入也不能保存为 .rfa。可载入族可以通过 .rfa 文件交换。


创建元素

创建墙

public Wall CreateWall(Document doc, Curve curve, Level level, WallType wallType)
{
    // 计算墙高度
    double height = 4000.0 / 304.8;        // 4000mm → 英尺

    // 创建墙
    Wall wall = Wall.Create(
        doc,
        curve,
        wallType.Id,
        level.Id,
        height,
        0.0,          // 底部偏移
        false,        // 是否翻转
        false         // 是否结构
    );

    return wall;
}

创建楼板

public Floor CreateFloor(Document doc, List<CurveLoop> profile, FloorType floorType, Level level)
{
    Floor floor = Floor.Create(
        doc,
        profile,
        floorType.Id,
        level.Id
    );

    return floor;
}

// 构建 CurveLoop(楼板轮廓)
CurveLoop profile = new CurveLoop();
profile.Append(Line.CreateBound(new XYZ(0, 0, 0), new XYZ(10, 0, 0)));
profile.Append(Line.CreateBound(new XYZ(10, 0, 0), new XYZ(10, 10, 0)));
profile.Append(Line.CreateBound(new XYZ(10, 10, 0), new XYZ(0, 10, 0)));
profile.Append(Line.CreateBound(new XYZ(0, 10, 0), new XYZ(0, 0, 0)));

放置族实例(门、窗、家具)

public FamilyInstance PlaceFamilyInstance(
    Document doc,
    FamilySymbol symbol,    // 族类型(如 "0915 × 2100mm" 门)
    XYZ location,            // 插入点
    Level level)
{
    // 激活族类型
    if (!symbol.IsActive)
        symbol.Activate();

    // 创建实例
    FamilyInstance instance = doc.Create.NewFamilyInstance(
        location,
        symbol,
        level,
        StructuralType.NonStructural
    );

    return instance;
}

基于线的族(如梁、管线)

public FamilyInstance PlaceBeam(
    Document doc,
    FamilySymbol beamType,
    XYZ startPoint,
    XYZ endPoint,
    Level level)
{
    if (!beamType.IsActive)
        beamType.Activate();

    FamilyInstance beam = doc.Create.NewFamilyInstance(
        Line.CreateBound(startPoint, endPoint),
        beamType,
        level,
        StructuralType.Beam
    );

    return beam;
}

获取族类型

// 获取特定族名称的所有类型
public List<FamilySymbol> GetFamilySymbols(Document doc, string familyName)
{
    return new FilteredElementCollector(doc)
        .OfClass(typeof(FamilySymbol))
        .Cast<FamilySymbol>()
        .Where(s => s.FamilyName == familyName)
        .ToList();
}

// 根据类别和名称获取族类型
public FamilySymbol? GetFamilySymbol(
    Document doc,
    BuiltInCategory category,
    string familyName,
    string typeName)
{
    return new FilteredElementCollector(doc)
        .OfClass(typeof(FamilySymbol))
        .OfCategory(category)
        .Cast<FamilySymbol>()
        .FirstOrDefault(s => s.FamilyName == familyName && s.Name == typeName);
}

// 获取项目中可用的门类型
var doorTypes = GetFamilySymbols(doc, "单扇门");
foreach (FamilySymbol doorType in doorTypes)
{
    Debug.Print($"{doorType.FamilyName} - {doorType.Name}");
}

修改元素位置

// 移动元素
public void MoveElement(Document doc, Element element, XYZ translation)
{
    ElementTransformUtils.MoveElement(doc, element.Id, translation);
}

// 旋转元素
public void RotateElement(Document doc, Element element, XYZ center, XYZ axis, double angle)
{
    Line axisLine = Line.CreateBound(center, center + axis);
    ElementTransformUtils.RotateElement(doc, element.Id, axisLine, angle);
}

// 镜像元素
public void MirrorElement(Document doc, Element element, Plane plane)
{
    ElementTransformUtils.MirrorElement(doc, element.Id, plane);
}

// 复制元素
public ICollection<ElementId> CopyElements(
    Document doc,
    ICollection<ElementId> elementIds,
    XYZ translation)
{
    return ElementTransformUtils.CopyElements(
        doc,
        elementIds,
        translation
    );
}

删除元素

// 删除单个元素
public void DeleteElement(Document doc, ElementId id)
{
    doc.Delete(id);
}

// 批量删除
public void DeleteElements(Document doc, ICollection<ElementId> ids)
{
    doc.Delete(ids);
}

// 检查元素是否可以被删除
// (有些系统族类型、当前活动的 Level 等不可删除)

🚨 删除操作必须在事务中进行。删除后元素 ID 立即失效,后续引用会导致异常。


标高与轴网

// 创建标高
public Level CreateLevel(Document doc, double elevationMeters)
{
    double elevationFeet = elevationMeters / 0.3048;

    Level level = Level.Create(
        doc,
        elevationFeet
    );

    // 设置名称
    level.Name = $"Level {elevationMeters:F1}m";

    return level;
}

// 获取所有标高(从低到高排序)
public List<Level> GetAllLevels(Document doc)
{
    return new FilteredElementCollector(doc)
        .OfClass(typeof(Level))
        .Cast<Level>()
        .OrderBy(l => l.Elevation)
        .ToList();
}

// 获取最近的标高
public Level? GetNearestLevel(Document doc, double elevationFeet)
{
    return GetAllLevels(doc)
        .OrderBy(l => Math.Abs(l.Elevation - elevationFeet))
        .FirstOrDefault();
}

// 创建轴网
public Grid CreateGrid(Document doc, XYZ start, XYZ end, string name)
{
    Line line = Line.CreateBound(start, end);
    Grid grid = Grid.Create(doc, line);
    grid.Name = name;
    return grid;
}

获取元素的几何数据

public void ExtractGeometry(Element element)
{
    Options geomOptions = new Options
    {
        ComputeReferences = true,
        IncludeNonVisibleObjects = false,
        DetailLevel = ViewDetailLevel.Medium
    };

    GeometryElement geomElem = element.get_Geometry(geomOptions);

    foreach (GeometryObject geomObj in geomElem)
    {
        // 实体(Solid)—— 具有体积
        if (geomObj is Solid solid && solid.Volume > 0)
        {
            double volume = solid.Volume;
            double surfaceArea = solid.SurfaceArea;

            foreach (Face face in solid.Faces)
            {
                double faceArea = face.Area;
            }
        }

        // 线(Line)—— 基于线的元素
        if (geomObj is Line line)
        {
            XYZ start = line.GetEndPoint(0);
            XYZ end = line.GetEndPoint(1);
            double length = line.Length;
        }

        // 几何实例(GeometryInstance)—— 嵌套族
        if (geomObj is GeometryInstance instance)
        {
            GeometryElement instanceGeom = instance.GetInstanceGeometry();
            // 递归处理
        }
    }
}

BoundingBox —— 包围盒

// 获取当前视图中的包围盒
BoundingBoxXYZ bbox = element.get_BoundingBox(doc.ActiveView);
if (bbox != null)
{
    XYZ min = bbox.Min;   // 左下前角
    XYZ max = bbox.Max;   // 右上后角
    XYZ center = (min + max) / 2;
}

// 获取元素的包围盒(需要传入视图 null 获取模型坐标)
BoundingBoxXYZ bbox2 = element.get_BoundingBox(null);

元素 ID 核心概念

Revit 中有多种标识符,每次打开项目可能变化:

类型 属性 说明
ElementId element.Id 会话内唯一,关闭文档后失效
UniqueId element.UniqueId 永久唯一,跨会话稳定(推荐持久化引用时使用)
IntegerValue element.Id.IntegerValue 32 位整数值
// 通过 UniqueId 获取元素(跨文档持久化引用)
string uid = "6a7b8c9d-1234-5678-...";
Element element = doc.GetElement(uid);    // 通常返回 null,需用其他方式

// UniqueId 格式包含 EpisodeId,通常用于 IFC 导出
// 跨会话持久化推荐存储 (CategoryId, TypeName, Level, Location) 组合信息重组

💡 如果要跨 Revit 会话持久化引用某个元素,不要存储 ElementId。存储元素的位置信息、类型和 UniqueId,在重新打开文档时通过这些信息重组查找。