第14章:组件化生成
第 02 章介绍了 ElementInstance 与 IsElementDefinition 配合实现的”定义-实例”模式——定义一个元素,然后在不同位置创建实例。这种模式对单一元素(如标准梁、标准柱)效果很好,但建筑项目中有大量复杂需求:如何定义一个由多种元素组合而成的可重用单元,并将其自适应地放置到不同尺寸、不同形状的区域内?
这就是 Elements.Components 要解决的问题。它提供了一个组件化生成框架,将”元素组合 + 放置规则”封装为可重用的 ComponentDefinition,然后通过锚点系统将其实例化到任意目标边界中。
14.1 组件化概念
Elements.Components 的核心模型由三个层次构成:
ComponentDefinition(组件定义)
├── ReferenceAnchors(参考锚点) ← 定义阶段的"原型坐标系"
├── Rules(放置规则列表) ← 决定如何生成子元素
│ ├── PositionPlacementRule ← 固定位置放置
│ ├── ArrayPlacementRule ← 阵列排列
│ ├── GridPlacementRule ← 网格排列
│ ├── PolylinePlacementRule ← 折线变形
│ ├── SizeBasedPlacementRule ← 尺寸驱动选型
│ └── ComponentPlacementRule ← 嵌套子组件
└── OrientationGuide(方向引导) ← 计算得出的旋转参考
ComponentInstance(组件实例)
└── Instances(子元素列表) ← Instantiate() 的输出
关键类定义:
namespace Elements.Components
{
public class ComponentDefinition : Element
{
// 内部规则列表
internal IList<IComponentPlacementRule> Rules { get; set; }
// 参考锚点——定义阶段使用的"模板坐标"
internal IList<Vector3> ReferenceAnchors { get; set; }
// 锚点位移——Instantiate 时根据目标锚点计算得出
internal IList<Vector3> AnchorDisplacements { get; set; }
// 方向引导——Instantiate 时根据锚点计算得出的旋转参考
internal Transform OrientationGuide { get; set; }
// 核心方法:根据目标锚点创建组件实例
public ComponentInstance Instantiate(
IList<Vector3> anchors,
Transform orientationGuide = null
);
}
}
14.1.1 锚点机制
锚点(Anchor)是连接”定义”与”实例”的桥梁。典型的工作流程是:
- 选择一个矩形边界作为参考区域,取其 5 个关键点作为参考锚点(4 个角 + 中心点)
- 在参考区域内排布各种元素,并记录每个元素”附着”在哪个锚点上
- 当给定一组新的目标锚点(例如更大、更小、旋转或扭曲的矩形),Instantiate 方法会:
- 计算参考锚点与目标锚点之间的位移向量
- 根据前两个锚点的方向变化计算旋转角度(OrientationGuide)
- 将每个规则中的元素按对应锚点变换到目标位置
锚点的数量、顺序在定义和实例化阶段必须一致。
14.2 ComponentDefinition 详解
14.2.1 构造函数
public ComponentDefinition(
IList<IComponentPlacementRule> rules, // 放置规则
IList<Vector3> referenceAnchors // 参考锚点
) : base(Guid.NewGuid(), null)
参数说明:
| 参数 | 类型 | 说明 |
|---|---|---|
rules |
IList<IComponentPlacementRule> |
一组放置规则,决定组件如何生成子元素 |
referenceAnchors |
IList<Vector3> |
定义阶段使用的参考锚点,通常为边界的角点和中心点 |
14.2.2 Instantiate 方法内部机制
Instantiate(anchors) 的执行流程:
Instantiate(anchors)
├── 1. 计算 AnchorDisplacements
│ 对每个参考锚点 i,找到对应的目标锚点,
│ 计算 displacement[i] = targetAnchors[i] - referenceAnchors[i]
│
├── 2. 计算 OrientationGuide
│ 如果提供了外部 orientationGuide → 使用它
│ 否则,根据前两个锚点的方向变化计算旋转角度
│ angle = Angle_2D(referenceVector, targetVector)
│
├── 3. 遍历所有 Rules,逐个调用 rule.Instantiate(this)
│ ├── PositionPlacementRule → 根据锚点位移放置单个元素
│ ├── ArrayPlacementRule → 基于变换后的路径排列阵列
│ ├── GridPlacementRule → 基于变换后的区域创建网格
│ ├── SizeBasedPlacementRule → 选择适合当前尺寸的元素
│ ├── PolylinePlacementRule → 创建变换后的折线
│ └── ComponentPlacementRule → 递归实例化子组件
│
└── 4. 返回 ComponentInstance(包含所有生成的元素)
14.2.3 锚点映射策略
源码中的锚点映射逻辑如下(简化):
for (int i = 0; i < ReferenceAnchors.Count; i++)
{
var referenceAnchor = ReferenceAnchors[i];
var matchingAnchor = anchors[i % anchors.Count];
AnchorDisplacements.Add(matchingAnchor - referenceAnchor);
}
每个参考锚点按索引一一对应到目标锚点。如果目标锚点数量不足,会循环使用(i % anchors.Count),但最佳实践是保持数量一致。
14.2.4 OrientationGuide 的自动计算
当不手动提供 orientationGuide 时,Instantiate 会根据前两个锚点的方向自动计算旋转:
var referenceVector = ReferenceAnchors[1] - ReferenceAnchors[0];
var targetVector = anchors[1] - anchors[0];
var angle = Angle_2D(referenceVector, targetVector) * 180 / Math.PI;
OrientationGuide = new Transform(Vector3.Origin, angle);
这意味着:改变前两个锚点的方向就能旋转整个组件。
14.3 放置规则体系
所有放置规则都实现 IComponentPlacementRule 接口:
public interface IComponentPlacementRule
{
string Name { get; set; }
List<Element> Instantiate(ComponentDefinition definition);
}
其中大部分规则还实现了 ICurveBasedComponentPlacementRule,表示它们基于一条可变形曲线:
public interface ICurveBasedComponentPlacementRule : IComponentPlacementRule
{
Polyline Curve { get; set; }
IList<int> AnchorIndices { get; set; } // 每个顶点对应哪个锚点
IList<Vector3> AnchorDisplacements { get; set; } // 每个顶点与锚点的偏移
}
14.3.1 PositionPlacementRule:指定位置放置
将单个元素放置在相对于某个锚点的固定偏移位置。
public class PositionPlacementRule : IComponentPlacementRule
{
public int AnchorIndex { get; set; } // 绑定到第几个锚点
public Transform AnchorTransform { get; set; } // 相对于锚点的偏移变换
public GeometricElement Definition { get; set; } // 要放置的元素
public PositionPlacementRule(
string name,
int anchorIndex,
GeometricElement definition,
Transform anchorOffset = null
);
}
实例化逻辑:Instantiate 时,根据该锚点的位移量和方向变换,计算出元素的最终 Transform,然后用 CreateInstance 生成一个实例。
工厂方法 FromClosestPoints 是最常用的创建方式——自动将每个元素分配给距离最近的锚点,并计算偏移量:
// 自动将 red_cube 和 blue_cube 绑定到最近的锚点
var rules = PositionPlacementRule.FromClosestPoints(
new[] { red_cube, blue_cube },
referencePoints
);
14.3.2 ArrayPlacementRule:阵列放置
沿一条折线等距排列元素,适用于栏杆、路灯、管线支架等场景。
public class ArrayPlacementRule : ICurveBasedComponentPlacementRule
{
public Polyline Curve { get; set; } // 阵列路径
public GeometricElement ElementDefinition { get; set; } // 要排列的元素
public SpacingConfiguration SpacingRule { get; set; } // 间距配置
public bool IsClosed { get; set; } // 路径是否闭合
public ArrayPlacementRule(
GeometricElement definition,
Polyline arrayPath,
SpacingConfiguration spacingRule,
IList<int> anchorIndices,
IList<Vector3> anchorDisplacements,
string name
);
}
间距配置 SpacingConfiguration 支持三种模式:
public enum SpacingMode
{
ByCount, // 按数量:Value = 元素个数
ByLength, // 按固定间距:Value = 间距距离
ByApproximateLength // 按近似间距:Value = 目标间距(自动调整为整除)
}
public struct SpacingConfiguration
{
public SpacingMode SpacingMode { get; set; }
public double Value { get; set; }
public SpacingConfiguration(SpacingMode mode, double value);
}
实例化逻辑:先通过 TransformPolyline 将参考路径变换到目标位置,然后使用 Grid1d 将路径按指定方式切分,在每个分隔点处创建元素实例。
14.3.3 GridPlacementRule:网格放置
在矩形区域内按网格排列元素,适用于幕墙面板、天花板、地板砖等场景。
public class GridPlacementRule : ICurveBasedComponentPlacementRule
{
public Polyline Curve { get; set; } // 网格区域(多边形)
public GridCellDefinition CellDefinition { get; set; } // 单元格定义
public Action<Grid2d> GridCreationRule { get; set; } // 自定义网格划分
}
public struct GridCellDefinition
{
public IEnumerable<GeometricElement> Elements { get; set; } // 放置在每格中的元素
public double CellWidth { get; set; } // 单元格宽度
public double CellLength { get; set; } // 单元格长度
}
实例化逻辑:先将边界多边形变换到目标位置,然后创建 Grid2d,按 GridCreationRule 划分网格。默认行为是 U 方向按 CellLength 划分,V 方向按 CellWidth 划分。最后在每个未裁剪的单元格中放置元素实例。
默认切片规则:
g.U.DivideByFixedLength(CellDefinition.CellLength, FixedDivisionMode.RemainderAtBothEnds);
g.V.DivideByFixedLength(CellDefinition.CellWidth, FixedDivisionMode.RemainderAtBothEnds);
14.3.4 PolylinePlacementRule:折线路径变形
创建一条随锚点变形的折线或多边形。它通常用于在组件中可视化边界轮廓(例如画出目标区域的边框),而非生成建筑元素。
public class PolylinePlacementRule : Element, ICurveBasedComponentPlacementRule
{
public Polyline Curve { get; set; }
public bool IsPolygon { get; set; }
}
TransformPolyline 静态方法是 所有基于曲线的规则的共用核心——它将参考多边形的每个顶点,按其绑定的锚点的位移量进行平移和旋转:
internal static List<Vector3> TransformPolyline(
ICurveBasedComponentPlacementRule rule,
ComponentDefinition definition
)
{
var newVertices = new List<Vector3>();
for (int i = 0; i < rule.Curve.Vertices.Count; i++)
{
var anchorIndex = rule.AnchorIndices[i];
var anchorForVertex = definition.ReferenceAnchors[anchorIndex];
var displacementForVertex = definition.AnchorDisplacements[anchorIndex];
var transform = new Transform(
definition.OrientationGuide.OfPoint(rule.AnchorDisplacements[i])
);
transform.Concatenate(new Transform(displacementForVertex));
newVertices.Add(transform.OfPoint(anchorForVertex));
}
return newVertices;
}
14.3.5 SizeBasedPlacementRule:尺寸驱动选型
根据可用空间大小,从一组候选元素中选择能放得下的最大元素。适用于需要根据空间自适应选择尺寸的场景,如根据房间大小选择不同的家具布局。
public class SizeBasedPlacementRule : Element, ICurveBasedComponentPlacementRule
{
public Polyline Curve { get; set; }
public List<(GeometricElement element, Polygon clearance)>
ElementsAndClearances { get; set; }
}
候选元素按 clearance 面积降序排列,实例化时遍历候选列表,找到第一个 clearance 多边形完全被目标边界覆盖的选项,即放置该元素。
14.3.6 ComponentPlacementRule:子组件嵌套
将另一个 ComponentDefinition 作为子组件嵌入当前组件,实现递归的组件树:
public class ComponentPlacementRule : ICurveBasedComponentPlacementRule
{
public ComponentDefinition Component { get; set; } // 子组件定义
public Polyline Curve { get; set; } // 子组件的边界曲线
public Func<List<Vector3>, List<Vector3>>
AnchorTransformer { get; set; } // 锚点变换函数
}
实例化时,先通过 TransformPolyline 变换曲线得到子组件的目标锚点,然后调用 Component.Instantiate(newVertices) 递归实例化。
14.4 ComponentInstance:组件实例
ComponentInstance 是一个轻量级的 Element,本质上是子元素的容器:
public class ComponentInstance : Element
{
public List<Element> Instances { get; set; } // 所有生成的子元素
public ComponentInstance(string name = null)
: base(Guid.NewGuid(), name)
{
Instances = new List<Element>();
}
}
在 Model.AddElement 的默认行为中,添加一个 ComponentInstance 时,其 Instances 中的元素会被递归收集并一并加入模型。
14.5 组件嵌套
ComponentPlacementRule 使 ComponentDefinition 可以包含其他 ComponentDefinition,形成组件树:
顶层 ComponentDefinition(办公室套间)
├── ArrayPlacementRule: 沿外墙排列办公桌
├── ComponentPlacementRule → 子组件:标准办公桌组
│ ├── PositionPlacementRule: 桌面
│ ├── PositionPlacementRule: 椅子
│ └── PositionPlacementRule: 侧柜
├── SizeBasedPlacementRule: 根据角落空间放置文件柜
└── PolylinePlacementRule: 画出边界轮廓
这种嵌套机制允许将常见的子布局封装为独立组件,在不同场景中复用。AnchorTransformer 回调提供了在锚点传入子组件前进行额外变换的能力,可用于镜像翻转、微调位置等需求。
14.6 实战示例
以下三个示例演示了组件化框架的核心用法。每个示例均为完整的可运行代码。
14.6.1 标准办公桌组件(ArrayPlacementRule)
创建一个”标准办公桌”的组件定义,包含桌面和椅子,然后沿一面墙等距排列 4 套:
using Elements;
using Elements.Components;
using Elements.Geometry;
using Elements.Geometry.Solids;
// --- 第 1 步:定义参考区域 ---
// 矩形区域:10m x 6m,使用 5 个参考锚点(4 角 + 中心)
var referenceAnchors = new List<Vector3>
{
new Vector3(0, 0, 0), // 左下角
new Vector3(10, 0, 0), // 右下角
new Vector3(10, 6, 0), // 右上角
new Vector3(0, 6, 0), // 左上角
new Vector3(5, 3, 0) // 中心点
};
// --- 第 2 步:创建桌面元素(1.6m x 0.8m 的桌面板)---
var deskProfile = new Profile(Polygon.Rectangle(1.6, 0.8));
var desk = new Mass(
deskProfile,
0.05, // 桌面厚度 5cm
new Material("橡木", new Color(0.6, 0.4, 0.2, 1.0)),
new Transform(0, 0, 0.75) // 离地 0.75m
);
// --- 第 3 步:创建椅子元素 ---
var chairSeatProfile = new Profile(Polygon.Rectangle(0.45, 0.45));
var chair = new Mass(
chairSeatProfile,
0.45, // 椅面高度
new Material("灰色织物", new Color(0.3, 0.3, 0.3, 1.0)),
new Transform(0, -0.5, 0.45) // 在桌面下方
);
// --- 第 4 步:定义阵列路径(沿 Y = 1 从 X=1 到 X=9)---
var arrayPath = new Polyline(new[]
{
new Vector3(1, 1, 0),
new Vector3(9, 1, 0)
});
// --- 第 5 步:创建放置规则 ---
// 5a. 桌面放在路径上每个节点处
var deskRule = ArrayPlacementRule.FromClosestPoints(
desk,
arrayPath,
new SpacingConfiguration(SpacingMode.ByCount, 4), // 排列 4 张
referenceAnchors,
"桌面阵列"
);
// 5b. 椅子随桌面偏移排列(在 Y 方向上偏移 -0.6m)
var chairArrayPath = new Polyline(new[]
{
new Vector3(1, 0.4, 0),
new Vector3(9, 0.4, 0)
});
var chairRule = ArrayPlacementRule.FromClosestPoints(
chair,
chairArrayPath,
new SpacingConfiguration(SpacingMode.ByCount, 4),
referenceAnchors,
"椅子阵列"
);
// 5c. 边界可视化(用 PolylinePlacementRule 画出参考区域边框)
var boundaryRule = PolylinePlacementRule.FromClosestPoints(
Polygon.Rectangle(new Vector3(0, 0, 0), new Vector3(10, 6)),
referenceAnchors,
"边界"
);
// --- 第 6 步:创建组件定义 ---
var officeDeskDef = new ComponentDefinition(
new IComponentPlacementRule[] { deskRule, chairRule, boundaryRule },
referenceAnchors
);
// --- 第 7 步:实例化到目标边界 ---
// 假设房间实际尺寸为 8m x 5m
var targetAnchors = new List<Vector3>
{
new Vector3(0, 0, 0),
new Vector3(8, 0, 0),
new Vector3(8, 5, 0),
new Vector3(0, 5, 0),
new Vector3(4, 2.5, 0)
};
var instance = officeDeskDef.Instantiate(targetAnchors);
// --- 第 8 步:添加到模型并导出 ---
var model = new Model();
model.AddElement(instance);
model.ToGlTF("office_desk.glb");
Console.WriteLine($"生成了 {instance.Instances.Count} 个子元素");
14.6.2 幕墙面板组件(GridPlacementRule)
创建一个幕墙面板组件,在给定的幕墙区域内按网格排列玻璃面板:
using Elements;
using Elements.Components;
using Elements.Geometry;
using Elements.Geometry.Solids;
// --- 第 1 步:定义参考区域(30m x 20m 幕墙)---
var referenceAnchors = new List<Vector3>
{
new Vector3(0, 0, 0),
new Vector3(30, 0, 0),
new Vector3(30, 20, 0),
new Vector3(0, 20, 0),
new Vector3(15, 10, 0)
};
// --- 第 2 步:定义幕墙面板元素 ---
// 标准面板:1.5m 宽 x 2m 高,厚度 0.1m
var panelProfile = new Profile(Polygon.Rectangle(1.5, 2.0));
var panel = new Mass(
panelProfile,
0.1,
new Material("玻璃", new Color(0.68, 0.85, 0.9, 0.6)),
new Transform()
)
{
Name = "幕墙面板"
};
// --- 第 3 步:定义网格单元格 ---
// 在 Grid2d 坐标系中:U 方向 = 水平(X),V 方向 = 垂直(Y)
var cellDef = new GridCellDefinition(
new[] { panel }, // 每个单元格中放置一个面板
cellWidth: 2.0, // V 方向(高)
cellLength: 1.5 // U 方向(宽)
);
// --- 第 4 步:创建网格放置规则 ---
var targetArea = Polygon.Rectangle(
new Vector3(1, 1, 0), // 幕墙左下角
new Vector3(29, 19, 0) // 幕墙右上角
);
var gridRule = GridPlacementRule.FromClosestPoints(
cellDef,
targetArea,
referenceAnchors,
"幕墙网格"
);
// --- 第 5 步:边界可视化 ---
var boundaryRule = PolylinePlacementRule.FromClosestPoints(
Polygon.Rectangle(new Vector3(0, 0, 0), new Vector3(30, 20)),
referenceAnchors,
"幕墙边界"
);
// --- 第 6 步:创建组件定义 ---
var curtainWallDef = new ComponentDefinition(
new IComponentPlacementRule[] { gridRule, boundaryRule },
referenceAnchors
);
// --- 第 7 步:实例化到不同尺寸的幕墙 ---
// 场景 A:原始尺寸(30m x 20m)
var anchorsA = new List<Vector3>
{
new Vector3(0, 0, 0),
new Vector3(30, 0, 0),
new Vector3(30, 20, 0),
new Vector3(0, 20, 0),
new Vector3(15, 10, 0)
};
var model = new Model();
model.AddElement(curtainWallDef.Instantiate(anchorsA));
model.ToGlTF("curtain_wall_A.glb");
Console.WriteLine($"幕墙 A 生成完成");
// 场景 B:旋转后的幕墙(30m x 15m,旋转 90 度)
var anchorsB = new List<Vector3>
{
new Vector3(0, 0, 0),
new Vector3(0, 30, 0), // 前两个锚点改为垂直方向
new Vector3(15, 30, 0),
new Vector3(15, 0, 0),
new Vector3(7.5, 15, 0)
};
var modelB = new Model();
modelB.AddElement(curtainWallDef.Instantiate(anchorsB));
modelB.ToGlTF("curtain_wall_B.glb");
Console.WriteLine("幕墙 B 生成完成");
14.6.3 楼梯栏杆组件(PolylinePlacementRule + ArrayPlacementRule)
创建一个楼梯栏杆组件,沿楼梯的折线轮廓放置栏杆立柱和扶手:
using Elements;
using Elements.Components;
using Elements.Geometry;
using Elements.Geometry.Solids;
// --- 第 1 步:定义参考锚点(楼梯的简化轮廓)---
// 楼梯从 (0,0) 到 (3,4) 到 (5,4),高程从 0 到 3m
var referenceAnchors = new List<Vector3>
{
new Vector3(0, 0, 0), // 楼梯起点
new Vector3(3, 0, 1.5), // 第一段中间转折
new Vector3(5, 0, 3.0), // 楼梯终点
new Vector3(0, 1, 0), // 栏杆外侧偏移参考
new Vector3(5, 1, 3.0) // 栏杆外侧偏移参考
};
// --- 第 2 步:创建栏杆立柱元素 ---
var postProfile = new Profile(Polygon.Rectangle(0.05, 0.05));
var post = new Mass(
postProfile,
1.1, // 立柱高度 1.1m
new Material("不锈钢", new Color(0.75, 0.75, 0.75, 1.0)),
new Transform()
)
{
Name = "栏杆立柱"
};
// --- 第 3 步:创建扶手元素(水平管)---
var railProfile = new Profile(
new Circle(Vector3.Origin, 0.03).ToPolygon(16)
);
var rail = new Mass(
railProfile,
0.2, // 横截面厚度
new Material("不锈钢", new Color(0.75, 0.75, 0.75, 1.0)),
new Transform()
)
{
Name = "扶手管"
};
// --- 第 4 步:定义栏杆的折线路径 ---
// 栏杆沿楼梯外侧布置
var railingPath = new Polyline(new[]
{
new Vector3(0.1, 0.8, 0.2), // 起点,偏移到楼梯外侧
new Vector3(3.0, 0.8, 1.7), // 中间转折
new Vector3(4.9, 0.8, 3.2) // 终点
});
// --- 第 5 步:创建阵列规则(立柱沿折线排列)---
var postRule = ArrayPlacementRule.FromClosestPoints(
post,
railingPath,
new SpacingConfiguration(SpacingMode.ByApproximateLength, 0.3), // 每30cm一根
referenceAnchors,
"立柱阵列"
);
// --- 第 6 步:创建折线规则(扶手管随楼梯折线变形)---
var railRule = PolylinePlacementRule.FromClosestPoints(
railingPath,
referenceAnchors,
"扶手路径"
);
// --- 第 7 步:边界可视化 ---
var boundaryRule = PolylinePlacementRule.FromClosestPoints(
new Polyline(new[]
{
new Vector3(0, 0, 0),
new Vector3(3, 0, 1.5),
new Vector3(5, 0, 3),
new Vector3(5, 1, 3),
new Vector3(0, 1, 0)
}),
referenceAnchors,
"楼梯轮廓"
);
// --- 第 8 步:创建组件定义 ---
var railingDef = new ComponentDefinition(
new IComponentPlacementRule[] { postRule, railRule, boundaryRule },
referenceAnchors
);
// --- 第 9 步:实例化到目标楼梯 ---
// 目标楼梯尺寸与参考一致(本例演示相同尺寸)
var targetAnchors = new List<Vector3>
{
new Vector3(0, 0, 0),
new Vector3(3, 0, 1.5),
new Vector3(5, 0, 3.0),
new Vector3(0, 1, 0),
new Vector3(5, 1, 3.0)
};
var instance = railingDef.Instantiate(targetAnchors);
// --- 第 10 步:添加到模型并导出 ---
var model = new Model();
model.AddElement(instance);
model.ToGlTF("stair_railing.glb");
Console.WriteLine($"栏杆组件生成完成,共 {instance.Instances.Count} 个子元素");
// 第 6 步的 PolylinePlacementRule 会生成 ModelCurve 用于可视化
// 立柱的 ArrayPlacementRule 会在路径上排列 ElementInstance
14.6.4 组件嵌套:办公室套间
将”标准办公桌”组件作为子组件,嵌套到更大的”办公室套间”组件中:
using Elements;
using Elements.Components;
using Elements.Geometry;
// 假设 officeDeskDef 是之前的"标准办公桌"组件定义(见 14.6.1)
// --- 第 1 步:定义办公室套间的参考锚点 ---
var roomAnchors = new List<Vector3>
{
new Vector3(0, 0, 0),
new Vector3(10, 0, 0),
new Vector3(10, 8, 0),
new Vector3(0, 8, 0),
new Vector3(5, 4, 0)
};
// --- 第 2 步:为每个子区域创建 ComponentPlacementRule ---
// 区域 A:左下角(4m x 5m)放办公桌组件
var subRegionA = Polygon.Rectangle(
new Vector3(0.5, 0.5, 0),
new Vector3(4.5, 5.5, 0)
);
var deskRule1 = ComponentPlacementRule.FromClosestPoints(
officeDeskDef, // 嵌套的办公桌组件定义
subRegionA,
roomAnchors,
"办公桌区 A"
);
// 区域 B:右下角同样放置办公桌
var subRegionB = Polygon.Rectangle(
new Vector3(5.5, 0.5, 0),
new Vector3(9.5, 5.5, 0)
);
var deskRule2 = ComponentPlacementRule.FromClosestPoints(
officeDeskDef,
subRegionB,
roomAnchors,
"办公桌区 B"
);
// --- 第 3 步:边界可视化 ---
var boundaryRule = PolylinePlacementRule.FromClosestPoints(
Polygon.Rectangle(new Vector3(0, 0, 0), new Vector3(10, 8)),
roomAnchors,
"房间边界"
);
// --- 第 4 步:创建办公室套间组件定义 ---
var officeSuiteDef = new ComponentDefinition(
new IComponentPlacementRule[] { deskRule1, deskRule2, boundaryRule },
roomAnchors
);
// --- 第 5 步:实例化到目标房间(14m x 10m)---
var targetRoomAnchors = new List<Vector3>
{
new Vector3(0, 0, 0),
new Vector3(14, 0, 0),
new Vector3(14, 10, 0),
new Vector3(0, 10, 0),
new Vector3(7, 5, 0)
};
var roomInstance = officeSuiteDef.Instantiate(targetRoomAnchors);
var model = new Model();
model.AddElement(roomInstance);
model.ToGlTF("office_suite.glb");
Console.WriteLine($"办公室套间生成完成");
14.7 小结
本章深入解析了 Elements.Components 组件化生成框架:
- ComponentDefinition 是组件的”蓝图”,由参考锚点和放置规则列表组成
- 锚点机制通过一一对应的索引实现从”定义空间”到”目标空间”的映射
- 六个标准放置规则覆盖了从简单定位到智能选型的各种布局需求
- ComponentInstance 是生成的元素容器,可通过
Model.AddElement一键加入模型 - ComponentPlacementRule 支持递归嵌套,实现组件树的复用和组合
- FromClosestPoints 工厂方法极大简化了规则的创建流程
组件化框架是 Elements 从”单个元素参数化”迈向”整体空间智能化”的关键一步。它将建筑空间的布局逻辑封装为可重用的数字化资产,为大规模自动化生成奠定了基础。