概念设计

本章讨论用于在族文档中创建复杂几何图形的Revit API的概念设计功能。通过添加新对象(点和通过这些点的样条曲线)来支持形状制作。可以分割、填充图案和嵌板化生成的曲面,以创建具有持久参数关系的可构建形状。

本节中的页面

  • 点和曲线对象
  • 形状
  • 曲面的几何化
  • 自适应构件
  • 创建.addin清单文件

点和曲线对象

参照点是在概念设计环境的XYZ工作空间中指定位置的元素。可以创建参照点来设计和打印直线、样条曲线和形状。可以将ReferencePoint添加到ReferencePointArray,然后用于创建CurveByPoints,而CurveByPoints又可用于创建形状。

下面的示例演示如何创建CurveByPoints对象。请参见下一节中的“创建放样形状”示例,了解如何从多个CurveByPoints对象创建形状。

代码区域14-1:创建新的CurveByPoints

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
//体量中创建
ReferencePointArray rpa = new ReferencePointArray();

XYZ xyz = document.Application.Create.NewXYZ(0, 0, 0);
ReferencePoint rp = document.FamilyCreate.NewReferencePoint(xyz);
rpa.Append(rp);

xyz = document.Application.Create.NewXYZ(0, 30, 10);
rp = document.FamilyCreate.NewReferencePoint(xyz);
rpa.Append(rp);

xyz = document.Application.Create.NewXYZ(0, 60, 0);
rp = document.FamilyCreate.NewReferencePoint(xyz);
rpa.Append(rp);

xyz = document.Application.Create.NewXYZ(0, 100, 30);
rp = document.FamilyCreate.NewReferencePoint(xyz);
rpa.Append(rp);

xyz = document.Application.Create.NewXYZ(0, 150, 0);
rp = document.FamilyCreate.NewReferencePoint(xyz);
rpa.Append(rp);

CurveByPoints curve = document.FamilyCreate.NewCurveByPoints(rpa);

图54:CurveByPoints曲线

参照点可以基于XYZ坐标创建,如上例所示,也可以相对于其他几何图元创建,以便当参照几何图元更改时,这些点会移动。这些点使用PointElementReference类的子类创建。这些子类是:

  • PointOnEdge
  • PointOnEdgeEdgeIntersection
  • PointOnEdgeFaceIntersection
  • PointOnFace
  • PointOnPlane

例如,上一个示例中的最后两行代码在CurveByPoints的中间创建了一个参考点。

可以使用模型线或参照线创建形状。模型线在创建过程中由形状“使用”,不再作为单独的图元存在。另一方面,参照线在形状创建后仍然存在,如果移动它们,可能会改变形状。虽然API没有ReferenceLine类,但可以使用ModelCurve.ChangeToReferenceLine()方法将模型线更改为参考线。

代码区域14-2:使用参照线创建形状

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
//demo失败(报错,无效操作)
private FormArray CreateRevolveForm(Document document)
{
FormArray revolveForms = null;

// Create one profile
ReferenceArray ref_ar = new ReferenceArray();

XYZ ptA = new XYZ(0, 0, 10);
XYZ ptB = new XYZ(100, 0, 10);
Line line = Line.CreateBound(ptA, ptB);
ModelCurve modelcurve = MakeLine(document, ptA, ptB);
ref_ar.Append(modelcurve.GeometryCurve.Reference);

ptA = new XYZ(100, 0, 10);
ptB = new XYZ(100, 100, 10);
modelcurve = MakeLine(document, ptA, ptB);
ref_ar.Append(modelcurve.GeometryCurve.Reference);

ptA = new XYZ(100, 100, 10);
ptB = new XYZ(0, 0, 10);
modelcurve = MakeLine(document, ptA, ptB);
ref_ar.Append(modelcurve.GeometryCurve.Reference);

// Create axis for revolve form
ptA = new XYZ(-5, 0, 10);
ptB = new XYZ(-5, 10, 10);
ModelCurve axis = MakeLine(document, ptA, ptB);

// make axis a Reference Line
axis.ChangeToReferenceLine();

// Typically this operation produces only a single form,
// but some combinations of arguments will create multiple froms from a single profile.
revolveForms = document.FamilyCreate.NewRevolveForms(true, ref_ar, axis.GeometryCurve.Reference, 0, Math.PI / 4);

return revolveForms;
}

public ModelCurve MakeLine(Document doc, XYZ ptA, XYZ ptB)
{
Autodesk.Revit.ApplicationServices.Application app = doc.Application;
// Create plane by the points
Line line = Line.CreateBound(ptA, ptB);
XYZ norm = ptA.CrossProduct(ptB);
if (norm.IsZeroLength()) norm = XYZ.BasisZ;
Plane plane = Plane.CreateByNormalAndOrigin(norm, ptB);
SketchPlane skplane = SketchPlane.Create(doc, plane);
// Create line here
ModelCurve modelcurve = doc.FamilyCreate.NewModelCurve(line, skplane);
return modelcurve;
}

图55:生成的Revolve表单

形状

创建形状

与族创建类似,概念设计环境提供了创建新形状的功能。可以创建以下类型的形状:拉伸、旋转、放样、放样融合、放样和曲面形状。体量族不使用族创建中使用的“混合”、“拉伸”、“旋转”、“放样”和“放样融合”类,而是将“形状”类用于所有类型的形状。

拉伸形状是从平面的闭合曲线回路创建的。旋转形状是根据轮廓和与轮廓在同一平面上的直线创建的,轮廓是围绕其旋转形状以创建三维形状的轴。放样形状是从沿沿着平面路径放样的二维轮廓创建的。放样融合是从多个轮廓创建的,每个轮廓都是平面的,沿沿着单个曲线放样。放样形状由位于不同平面上的两个或多个截面轮廓创建。单个曲面形状是从轮廓创建的,类似于拉伸,但不指定高度。

下面的示例创建一个简单的拉伸形状。请注意,由于用于创建形状的ModelCurves不会转换为参照线,因此它们将由生成的形状使用。

代码区域14-3:创建拉伸形状

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
private Form CreateExtrusionForm(Autodesk.Revit.DB.Document document)
{
Form extrusionForm = null;

// Create one profile
ReferenceArray ref_ar = new ReferenceArray();

XYZ ptA = new XYZ(10, 10, 0);
XYZ ptB = new XYZ(90, 10, 0);
ModelCurve modelcurve = MakeLine(document, ptA, ptB);
ref_ar.Append(modelcurve.GeometryCurve.Reference);

ptA = new XYZ(90, 10, 0);
ptB = new XYZ(10, 90, 0);
modelcurve = MakeLine(document, ptA, ptB);
ref_ar.Append(modelcurve.GeometryCurve.Reference);

ptA = new XYZ(10, 90, 0);
ptB = new XYZ(10, 10, 0);
modelcurve = MakeLine(document, ptA, ptB);
ref_ar.Append(modelcurve.GeometryCurve.Reference);

// The extrusion form direction
XYZ direction = new XYZ(0, 0, 50);

extrusionForm = document.FamilyCreate.NewExtrusionForm(true, ref_ar, direction);

int profileCount = extrusionForm.ProfileCount;

return extrusionForm;
}

public ModelCurve MakeLine(Document doc, XYZ ptA, XYZ ptB)
{
Autodesk.Revit.ApplicationServices.Application app = doc.Application;
// Create plane by the points
Line line = Line.CreateBound(ptA, ptB);
XYZ norm = ptA.CrossProduct(ptB);
if (norm.IsZeroLength()) norm = XYZ.BasisZ;
Plane plane = Plane.CreateByNormalAndOrigin(norm, ptB);
SketchPlane skplane = SketchPlane.Create(doc, plane);
// Create line here
ModelCurve modelcurve = doc.FamilyCreate.NewModelCurve(line, skplane);
return modelcurve;
}

图56:所得拉伸形状

下面的示例显示如何使用一系列CurveByPoints对象创建放样形状。

代码区域14-4:创建放样形状

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
private Form CreateLoftForm(Autodesk.Revit.Document document)
{
Form loftForm = null;

ReferencePointArray rpa = new ReferencePointArray();
ReferenceArrayArray ref_ar_ar = new ReferenceArrayArray();
ReferenceArray ref_ar = new ReferenceArray();
ReferencePoint rp = null;
XYZ xyz = null;

// make first profile curve for loft
xyz = document.Application.Create.NewXYZ(0, 0, 0);
rp = document.FamilyCreate.NewReferencePoint(xyz);
rpa.Append(rp);

xyz = document.Application.Create.NewXYZ(0, 50, 10);
rp = document.FamilyCreate.NewReferencePoint(xyz);
rpa.Append(rp);

xyz = document.Application.Create.NewXYZ(0, 100, 0);
rp = document.FamilyCreate.NewReferencePoint(xyz);
rpa.Append(rp);

CurveByPoints cbp = document.FamilyCreate.NewCurveByPoints(rpa);
ref_ar.Append(cbp.GeometryCurve.Reference);
ref_ar_ar.Append(ref_ar);
rpa.Clear();
ref_ar = new ReferenceArray();

// make second profile curve for loft
xyz = document.Application.Create.NewXYZ(50, 0, 0);
rp = document.FamilyCreate.NewReferencePoint(xyz);
rpa.Append(rp);

xyz = document.Application.Create.NewXYZ(50, 50, 30);
rp = document.FamilyCreate.NewReferencePoint(xyz);
rpa.Append(rp);

xyz = document.Application.Create.NewXYZ(50, 100, 0);
rp = document.FamilyCreate.NewReferencePoint(xyz);
rpa.Append(rp);

cbp = document.FamilyCreate.NewCurveByPoints(rpa);
ref_ar.Append(cbp.GeometryCurve.Reference);
ref_ar_ar.Append(ref_ar);
rpa.Clear();
ref_ar = new ReferenceArray();

// make third profile curve for loft
xyz = document.Application.Create.NewXYZ(75, 0, 0);
rp = document.FamilyCreate.NewReferencePoint(xyz);
rpa.Append(rp);

xyz = document.Application.Create.NewXYZ(75, 50, 5);
rp = document.FamilyCreate.NewReferencePoint(xyz);
rpa.Append(rp);

xyz = document.Application.Create.NewXYZ(75, 100, 0);
rp = document.FamilyCreate.NewReferencePoint(xyz);
rpa.Append(rp);

cbp = document.FamilyCreate.NewCurveByPoints(rpa);
ref_ar.Append(cbp.GeometryCurve.Reference);
ref_ar_ar.Append(ref_ar);

loftForm = document.FamilyCreate.NewLoftForm(true, ref_ar_ar);

return loftForm;
}

图57:生成的放样形状

形状修改

一旦创建,可以通过改变形状的子元素(即面、边、曲线或顶点)或整个轮廓来修改形状。修改表单的方法包括:

  • AddEdge
  • AddProfile
  • DeleteProfile
  • DeleteSubElement
  • MoveProfile
  • MoveSubElement
  • RotateProfile
  • RotateSubElement
  • ScaleSubElement

此外,可以通过添加边或轮廓来修改形状,然后可以使用上面列出的方法进行修改。

下面的示例将给定形状的第一条轮廓曲线移动指定的偏移量。相应的图显示了将此代码应用于上一个示例中的放样形式的结果。

代码区域14-5:移动轮廓

1
2
3
4
5
6
7
8
9
10
11
12
13
public void MoveForm(Form form)
{
int profileCount = form.ProfileCount;
if (form.ProfileCount > 0)
{
int profileIndex = 0; // modify the first form only
if (form.CanManipulateProfile(profileIndex))
{
XYZ offset = new XYZ(-25, 0, 0);
form.MoveProfile(profileIndex, offset);
}
}
}

图58:修改后的放样形式

下一个示例演示如何移动给定形状的单个顶点。相应的图演示了此代码对前面的拉伸形状示例的影响

代码区域14-6:移动子元素

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public void MoveSubElement(Form form)
{
if (form.ProfileCount > 0)
{
int profileIndex = 0; // get first profile
ReferenceArray ra = form.get_CurveLoopReferencesOnProfile(profileIndex, 0);
foreach (Reference r in ra)
{
ReferenceArray ra2 = form.GetControlPoints(r);
foreach (Reference r2 in ra2)
{
Point vertex = document.GetElement(r2).GetGeometryObjectFromReference(r2) as Point;

XYZ offset = new XYZ(0, 15, 0);
form.MoveSubElement(r2, offset);
break; // just move the first point
}
}
}
}

图59:修改拉伸的形状

表面的几何化

分割表面

形状的面可以用UV网格划分。可以使用DividedSurface.GetReferencesWithDividedSurface()和DividedSurface.GetDividedSurfaceForReference()方法(如后续示例所示)访问分割曲面的数据,也可以在形状上创建新的分割曲面,如下所示。

代码区域14-7:划分表面

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public void DivideSurface(Document document, Form form)
{
Autodesk.Revit.ApplicationServices.Application application = document.Application;
Options opt = application.Create.NewGeometryOptions();
opt.ComputeReferences = true;

Autodesk.Revit.DB.GeometryElement geomElem = form.get_Geometry(opt);
foreach (GeometryObject geomObj in geomElem)
{
Solid solid = geomObj as Solid;
foreach (Face face in solid.Faces)
{
if (face.Reference != null)
{
DividedSurface ds = DividedSurface.Create(document,face.Reference);
// create a divided surface with fixed number of U and V grid lines
SpacingRule srU = ds.USpacingRule;
srU.SetLayoutFixedNumber(16, SpacingRuleJustification.Center, 0, 0);

SpacingRule srV = ds.VSpacingRule;
srV.SetLayoutFixedNumber(24, SpacingRuleJustification.Center, 0, 0);

break; // just divide one face of form
}
}
}
}

图60:由UV网格划分的形状面

通过指定DividedSurface的USpacing和VSpacing属性,可以为U和V网格线定义SpacingRule,方法是指定固定的网格数(如上例所示)、网格之间的固定距离或网格之间的最小或最大间距。每个间距规则都需要其他信息,例如对正和网格旋转。

图案化表面

可以对分割的曲面进行阵列。任何内置平铺填充图案都可以应用于分割表面。平铺图案是指定给DividedSurface的ElementType。平铺图案根据UV栅格布局应用于曲面,因此更改DividedSurface的USpacing和VSpacing属性将影响图案化曲面的显示方式。

下面的示例演示如何使用OctagonRotate图案覆盖分割曲面。相应的图显示了将其应用于上一示例中的分割曲面时的外观。注意这个例子还演示了如何在窗体上获取DividedSurface。

代码区域14-8:图案化表面

1
2
3
4
5
6
7
8
9
10
public void TileSurface(Document document, Form form)
{
// cover surface with OctagonRotate tile pattern
TilePatterns tilePatterns = document.Settings.TilePatterns;
foreach (Reference r in DividedSurface.GetReferencesWithDividedSurfaces(form))
{
DividedSurface ds = DividedSurface.GetDividedSurfaceForReference(document, r);
ds.ChangeTypeId(tilePatterns.GetTilePattern(TilePatternsBuiltIn.OctagonRotate).Id);
}
}

图61:应用于分割表面的平铺图案

除了将内置的平铺填充图案应用于分割表面之外,还可以使用“基于幕墙嵌板填充图案的.rft”样板创建自己的体量嵌板族。然后,可以使用DividedSurface.ChangeTypeId()方法将这些嵌板族载入体量族并应用于分割曲面。

“族”的下列属性特定于幕墙嵌板族:

  • IsCurtainPanelFamily
  • CurtainPanelHorizontalSpacing -分隔网格的水平间距
  • CurtainPanelVerticalSpacing -分隔网格的垂直间距
  • CurtainPanelTilePattern -平铺图案的选择

下面的示例演示如何编辑体量嵌板族,然后将其应用于概念体量文档中的形状。若要运行此示例,请首先使用“Curtain Panel Pattern Based.rft”样板创建一个新的族文档。

代码区域14-9:编辑幕墙嵌板族

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
Family family = document.OwnerFamily;
if (family.IsCurtainPanelFamily == true &&
family.CurtainPanelTilePattern == TilePatternsBuiltIn.Rectangle)
{
// first change spacing of grids in family document
family.CurtainPanelHorizontalSpacing = 20;
family.CurtainPanelVerticalSpacing = 30;

// create new points and lines on grid
Autodesk.Revit.ApplicationServices.Application app = document.Application;
FilteredElementCollector collector = new FilteredElementCollector(document);
ICollection collection = collector.OfClass(typeof(ReferencePoint)).ToElements();
int ctr = 0;
ReferencePoint rp0 = null, rp1 = null, rp2 = null, rp3 = null;
foreach (Autodesk.Revit.DB.Element e in collection)
{
ReferencePoint rp = e as ReferencePoint;
switch (ctr)
{
case 0:
rp0 = rp;
break;
case 1:
rp1 = rp;
break;
case 2:
rp2 = rp;
break;
case 3:
rp3 = rp;
break;
}
ctr++;
}

ReferencePointArray rpAr = new ReferencePointArray();
rpAr.Append(rp0);
rpAr.Append(rp2);
CurveByPoints curve1 = document.FamilyCreate.NewCurveByPoints(rpAr);
PointLocationOnCurve pointLocationOnCurve25 = new PointLocationOnCurve(PointOnCurveMeasurementType.NormalizedCurveParameter, 0.25, PointOnCurveMeasureFrom.Beginning);
PointOnEdge poeA = app.Create.NewPointOnEdge(curve1.GeometryCurve.Reference, pointLocationOnCurve25);
ReferencePoint rpA = document.FamilyCreate.NewReferencePoint(poeA);
PointLocationOnCurve pointLocationOnCurve75 = new PointLocationOnCurve(PointOnCurveMeasurementType.NormalizedCurveParameter, 0.75, PointOnCurveMeasureFrom.Beginning);
PointOnEdge poeB = app.Create.NewPointOnEdge(curve1.GeometryCurve.Reference, pointLocationOnCurve75);
ReferencePoint rpB = document.FamilyCreate.NewReferencePoint(poeB);

rpAr.Clear();
rpAr.Append(rp1);
rpAr.Append(rp3);
CurveByPoints curve2 = document.FamilyCreate.NewCurveByPoints(rpAr);
PointOnEdge poeC = app.Create.NewPointOnEdge(curve2.GeometryCurve.Reference, pointLocationOnCurve25);
ReferencePoint rpC = document.FamilyCreate.NewReferencePoint(poeC);
PointOnEdge poeD = app.Create.NewPointOnEdge(curve2.GeometryCurve.Reference, pointLocationOnCurve75);
ReferencePoint rpD = document.FamilyCreate.NewReferencePoint(poeD);
}
else
{
throw new Exception("Please open a curtain family document before calling this command.");
}

图62:幕墙嵌板族

图63:指定给分割表面的幕墙嵌板

自适应构件

自适应组件旨在处理组件需要灵活地适应许多独特的上下文条件的情况。例如,自适应构件可用于通过排列符合用户定义约束的多个构件生成的重复系统中。

下面的代码显示如何将自适应构件族的实例创建到体量族中,并以数学方式设置每个点的位置。

代码区域:创建自适应构件族的实例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
private void CreateAdaptiveComponentInstance(Document document, FamilySymbol symbol)
{
// Create a new instance of an adaptive component family
FamilyInstance instance = AdaptiveComponentInstanceUtils.CreateAdaptiveComponentInstance(document, symbol);

// Get the placement points of this instance
var placePointIds = new List<ElementId>();
placePointIds = AdaptiveComponentInstanceUtils.GetInstancePlacementPointElementRefIds(instance).ToList();
double x = 0;

// Set the position of each placement point
foreach (ElementId id in placePointIds)
{
ReferencePoint point = document.GetElement(id) as ReferencePoint;
point.Position = new Autodesk.Revit.DB.XYZ(10*x, 10*Math.Cos(x), 0);
x += Math.PI / 6;
}
}

若要批处理创建自适应组件,可以使用FamilyInstanceCreationData构造函数的重载,该构造函数接受两个参数-FamilySymbol和要初始化自适应实例的XYZ自适应点列表。结合使用Autodesk. Rev. Creation. ItemFactoryBase. NewFamilyInstances 2()方法(该方法采用FamilyInstanceCreationData对象列表),可以一次添加多个自适应构件。这可能比逐个放置单个自适应组件更有效。

创建.addin清单文件

HelloWorld.dll文件出现在项目输出目录中。如果要在Revit中调用应用程序,请创建清单文件以将其注册到Revit中。

创建清单文件

  1. 在记事本中创建一个新的文本文件。

  2. 增加以下案文:

    代码区域30-10:为外部命令创建.addin清单文件

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    <?xml version="1.0" encoding="utf-8" standalone="no"?>


    HelloWorld
    HelloWorld.HelloWorld
    HelloWorld
    Show Hello World.
    AlwaysVisible
    C:\Samples\HelloWorld\HelloWorld\bin\Debug\HelloWorld.dll
    239BD853-36E4-461f-9171-C5ACEDA4E723
    ADSK
    Autodesk, Inc, www.autodesk.com

注意:FullClassName包括在项目属性的“应用程序”选项卡上找到的根命名空间。

将文件保存为HelloWorld.addin并将其放在以下位置:

  • C:\ProgramData\Autodesk\Revit\Addins\2018\

有关使用清单文件的更多详细信息,请参阅加载项集成。

注:翻译自Revit API Developers Guide