一、前言
几何对象中,Face
就先跳过了,这里讲讲如何使用 Revit API 中 Solid 操作相关类。
- 使用
SolidUtils
,主要是复制和移动。 - 使用
GeometryCreationUtilities
,生成Solid。 - 使用
SolidSolidCutUtils
和BooleanOperationsUtils
,进行Solid剪切个布尔操作(交并差)。 - 使用
DirectShape
,将Solid包装,放置到项目中。 - 使用
BRepBuilder
,基于边、面,生成任意形状的几何体。但是,该方法的使用相当笨拙和繁琐,它的设计初衷是将现有的几何体转换到 Revit 中,并对其进行相当彻底的输入几何体验证1。应该使用GeometryCreationUtilities
工具类来生成模型。
二、SolidUtils
SolidUtils
工具类,有5个方法。
Clone | 复制一个Solid |
---|---|
CreateTransformed | 对Solid应用变换矩阵。 Solid使用边和面表示,本身没有Transform。这里是对面和边进行变换。 |
SplitVoluems | 分割Solid。分割方式,未测试。 |
IsValidForTessellation TessellateSolidOrShell | 进行三角剖分。 |
这里提到了Shell
,这是个啥玩意儿?
三、GeometryCreationUtilities
GeometryCreationUtilities
是主要的创建Solid的手段。
这里,我们先了解一下Revit族创建工具栏上的指令:
- 拉伸:
Extrusion
- 融合:
Blend
- 旋转:
Revolution
- 放样:
Sweep
- 放样融合:
SweepBlend
以上接口,都派生自GenericForm
类,只能在族文档中使用,是专门用来创建族的。
回到几何创建工具类,也就是采用这些操作生成Solid,不同的是,这里多了个创建Loft
的方法。
3.1. Loft
使用方法CreateLoftGeometry
创建。
public static Solid CreateLoftGeometry(
IList<CurveLoop> profileLoops,
SolidOptions solidOptions
)
我不知道这里的【Loft】怎么翻译的,但效果上有点像是直纹曲面(RuledSurfece
),该方法也可以实现融合、放样的效果。
3.2. 拉伸
public static Solid CreateExtrusionGeometry(
IList<CurveLoop> profileLoops, // 轮廓
XYZ extrusionDir, // 仅表示方向,要求很宽松,不与轮廓面平行即可。
double extrusionDist, // 拉伸的长度
SolidOptions solidOptions
)
注意:CurveLoop本身需要满足条件:①共面,②不存在线交叉。
轮廓中,多个CurveLoop也不能有交叉。
3.3. 放样
public static Solid CreateSweptGeometry(
CurveLoop sweepPath, // 放样路径
int pathAttachmentCrvIdx, // 轮廓面所在的路径段的下标
double pathAttachmentParam, // 轮廓面与路径段的交点,该点在线上的参数化值
IList<CurveLoop> profileLoops, // 轮廓
SolidOptions solidOptions
)
这个方法复杂了一些,要求较多。
- 轮廓所在面需要与路径能够垂直(交点处垂直)
- 需要指定交点位于路径的第几条线上。
- 需要算到交点在该线上参数化值。关于参数化值,请看【这篇】。
虽然计算上比较简单,但为了方便生成,我们可以简单的将面放置到第1条线上,放在路径起点位置。
这么做还有一个原因,就是族编辑中的放样要求比较复杂,可以通过这样的计算简化一些。
族的Sweep
没有构造函数,创建方法在Creation命名空间中,具体的使用,后面再讲。
3.4. 旋转
public static Solid CreateRevolvedGeometry(
Frame coordinateFrame, // (正交)坐标系,确定 XYZ 三个轴。
IList<CurveLoop> profileLoops, // 旋转轮廓
double startAngle, // 起始角度,从XZ面开始,绕Z轴逆时针旋转
double endAngle, // 终止角度
SolidOptions solidOptions
)
这个方法的问题点主要在坐标系和轮廓上。
轮廓要求:①必须位于坐标系XZ面上,②必须在XZ面,X轴正方向那一侧。
给定 轮廓和旋转轴时(轴位于轮廓所在面上),一种计算方式:计算出一个Frame去配合轮廓。
- Frame.BaseZ:以旋转轴的方向作为Frame的Z轴;
- Frame.BaseY:以轮廓面的法向作为Frame的Y轴;
- Frame.BaseX:Y叉乘Z得到X轴;
- Frame.Origin:旋转轴上任意一点。
// 伪代码
var dirZ = line.Direction;
var dirY = curveloops.First().GetPlane().Normal;
var dirX = dirY.CrossProduct(dirZ);
Frame frame = new Frame(line.Origin, dirX, dirY, dirZ);
Solid solid = GeometryCreationUtilities.CreateRevolvedGeometry(frame, curveloops, start, end);
当给定的轴不在轮廓面上(无法满足轮廓位于XZ面),或者轴与轮廓有交叉(无法满足轮廓位于X轴正向),上面的计算就不行了。
此时非要生成的话,要么给轮廓投影,要么改旋转轴位置,然后用上面的方法。
3.4. 融合、放样融合
public static Solid CreateBlendGeometry(
CurveLoop firstLoop, // 轮廓1
CurveLoop secondLoop, // 轮廓2
ICollection<VertexPair> vertexPairs, // 轮廓间,点的配对关系,可为 null
SolidOptions solidOptions
)
融合与放样融合,两者API使用上没什么差别,这里只看融合。
这里要吐槽了,CreateBlendGeometry(..)
方法的要求比族编辑的Blend
使用简单多了,Blend要求上下轮廓面平行,而这里就不需要了。
轮廓问题不大,重点是**VertexPair**
的计算。
好在可以填null
,大部分情况下Revit自己就可以处理,但对于诸如 扭曲、圆转方、上下轮廓边数不等 等情况时,自带处理的结果就不能满足需求了,那就得根据需求写匹配逻辑。
那么这里,就只讲点配对是怎么回事儿了。
3.4.1. 轮廓间点的匹配(VertexPair
)
这时一个圆轮廓和方轮廓的融合,左边的是我期望的,右边是vertexPairs参数填null的效果。
下图是融合时,轮廓上顶点之间的对应关系。
- 可以看出,上下轮廓都是4条边组成,即均有4个顶点。
- 点上的数字,表示的是轮廓上,顶点集合的下标。轮廓中第一条边的起点,即是第一个顶点。
所以,点的匹配关系是于轮廓的状态(边数目,第一条边起始位置)相关的。
在处理 弧轮廓与直线轮廓 的融合时,这里又有一个不太好的处理方法:直接将弧细分(
Tessellate
),然后不设置点对(null
),这就完工了。
弊端是会出现很多的边,而且可能不会那么准确。
四、Solid 布尔操作
4.1. SolidSolidCutUtils
看名称,这就是个“剪切”操作API。对应于Revit工具栏上的剪切功能。
Revit工具栏上的剪切,是2个剪切工具类的集合:
InstanceVoidCutUtils
和SolidSolidCutUtils
。连接,是类:
JoinGeometryUtils
。
那么这个能进行Solid之间的布尔操作吗?答案是:不能。
那为啥要写在这里呢?答:这玩意儿名字取的好迷惑,写这儿提示下是用来做“剪切”的。
4.2. BooleanOperationsUtils
实体间的布尔操作,主要就两个方法:
CutWithHalfSpace :用一个平面去切 | ![]() |
---|---|
ExecuteBooleanOperation :两个Solid之间的交、并、差 | ![]() |
Revit会对模型间的操作进行验证,在剪切或布尔操作时,很容易会出现这样的错误:
- 无法从墙外剪切 [符号] 的实例。
- 无法剪切具有实例的主体。
- 屋顶无法剪切。
- 无法剪切连接的图元。
- 无法使图元保持连接。
这类错误,Revit将其归类为“切割或连接几何图形失败”,主要原因是操作后,产生了很短的边(小于0.79mm)。
一个不太好的处理方法,就是为 剪切体/被剪切体 添加一个小的偏移。
五、DirectShape
DirectShape
用于将一个几何形状做成在Revit中展示出来,展示的同时还能够为其添加一个类型。
// 伪代码
Solid sphere = GeometryCreationUtilities.CreateRevolvedGeometry(frame, new CurveLoop[] { curveLoop }, 0, 2 * Math.PI, options);
using (Transaction t = new Transaction(doc, "Create sphere direct shape"))
{
t.Start();
// create direct shape and assign the sphere shape
DirectShape ds = DirectShape.CreateElement(doc, new ElementId(BuiltInCategory.OST_GenericModel));
//ds.ApplicationId = "Application id";
//ds.ApplicationDataId = "Geometry object id";
ds.SetShape(new GeometryObject[] { sphere });
t.Commit();
}
上面的代码截取自Revti API文档,使用起来比较简单。
唯一需要注意的是,当传入多个Solid时,可能无法创建成功。原因可上面的一样,处理方式也一样。
六、总结
下一篇写BRepBuilder
。