Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...

Python 是一种广泛使用的编程语言,其流行程度与其语法风格有很大关系。它的可读性很高,因此比许多其他语言更易于学习。Python 支持模块和软件包,可以嵌入到现有应用程序中。有关如何启动和运行 Python 的信息,请访问 Python.org 上的“快速入门”页面(这是一个很好的资源)。
在本节中,您将找到一系列有关使用 DesignScript 创建几何体的课程。接下来,将 DesignScript 示例复制到 Dynamo 代码块。
// copy this code into a Code Block
// to start writing DesignScript
x = "Let's create some geometry!";Dynamo 标准几何体库中最简单的几何对象是一个点。所有几何体均使用称为构造函数的特殊函数创建,每个函数都返回该特定几何体类型的新实例。在 Dynamo 中,构造函数以对象类型的名称(在本例中为“Point”)开始,然后是构造方法。要创建由 x、y 和 z 笛卡尔坐标指定的三维点,请使用 ByCoordinates 构造函数:
// create a point with the following x, y, and z
// coordinates:
x = 10;
y = 2.5;
z = -6;
p = Point.ByCoordinates(x, y, z);Dynamo 中的构造函数通常使用前缀为“By”指定,调用这些函数将返回该类型的新创建对象。此新创建的对象存储在等号左侧命名的变量中。
大多数对象都有许多不同的构造函数,我们可以使用 BySphericalCoordinates 构造函数创建位于球体上的点,由球体的半径、第一个旋转角度和第二个旋转角度(以度为单位指定)指定:
// create a point on a sphere with the following radius,
// theta, and phi rotation angles (specified in degrees)
radius = 5;
theta = 75.5;
phi = 120.3;
cs = CoordinateSystem.Identity();
p = Point.BySphericalCoordinates(cs, radius, theta,
phi);点可用于构造更大尺寸的几何图形(例如直线)。我们可以使用 ByStartPointEndPoint 构造函数在两点之间创建“直线”对象:
// create two points:
p1 = Point.ByCoordinates(3, 10, 2);
p2 = Point.ByCoordinates(-15, 7, 0.5);
// construct a line between p1 and p2
l = Line.ByStartPointEndPoint(p1, p2);同样,直线可用于创建更多维的曲面几何体,例如使用 Loft 构造函数,该构造函数可获取一系列直线或曲线,并在它们之间内插曲面。
// create points:
p1 = Point.ByCoordinates(3, 10, 2);
p2 = Point.ByCoordinates(-15, 7, 0.5);
p3 = Point.ByCoordinates(5, -3, 5);
p4 = Point.ByCoordinates(-5, -6, 2);
p5 = Point.ByCoordinates(9, -10, -2);
p6 = Point.ByCoordinates(-11, -12, -4);
// create lines:
l1 = Line.ByStartPointEndPoint(p1, p2);
l2 = Line.ByStartPointEndPoint(p3, p4);
l3 = Line.ByStartPointEndPoint(p5, p6);
// loft between cross section lines:
surf = Surface.ByLoft([l1, l2, l3]);曲面也可用于创建更多维的实体几何体,例如通过按指定距离加厚曲面。许多对象都附加了函数(称为方法),程序员可以对该特定对象执行命令。所有几何图形通用的方法包括 Translate 和 Rotate,分别按指定的量平移(移动)和旋转几何图形。曲面具有 Thicken 方法,该方法采用单个输入,即指定曲面的新厚度的数字。
p1 = Point.ByCoordinates(3, 10, 2);
p2 = Point.ByCoordinates(-15, 7, 0.5);
p3 = Point.ByCoordinates(5, -3, 5);
p4 = Point.ByCoordinates(-5, -6, 2);
l1 = Line.ByStartPointEndPoint(p1, p2);
l2 = Line.ByStartPointEndPoint(p3, p4);
surf = Surface.ByLoft([l1, l2]);
// true indicates to thicken both sides of the Surface:
solid = surf.Thicken(4.75, true);交点 命令可以从较高维度的对象提取较低维度的几何图形。提取的较低纬度几何图形可以在几何图形创建、提取和重建的循环过程中形成较高维度几何图形的基础。在本示例中,我们使用生成的实体来创建曲面,并使用曲面来创建曲线。
p1 = Point.ByCoordinates(3, 10, 2);
p2 = Point.ByCoordinates(-15, 7, 0.5);
p3 = Point.ByCoordinates(5, -3, 5);
p4 = Point.ByCoordinates(-5, -6, 2);
l1 = Line.ByStartPointEndPoint(p1, p2);
l2 = Line.ByStartPointEndPoint(p3, p4);
surf = Surface.ByLoft([l1, l2]);
solid = surf.Thicken(4.75, true);
p = Plane.ByOriginNormal(Point.ByCoordinates(2, 0, 0),
Vector.ByCoordinates(1, 1, 1));
int_surf = solid.Intersect(p);
int_line = int_surf.Intersect(Plane.ByOriginNormal(
Point.ByCoordinates(0, 0, 0),
Vector.ByCoordinates(1, 0, 0)));对 NurbsCurve 的二维模拟是 NurbsSurface,与自由形式的 NurbsCurve 一样,可以使用两种基本方法构建 NurbsSurface:输入一组基点并在它们之间内插 Dynamo,然后明确指定曲面的控制点。当设计师确切知道曲面需要的形状或者设计需要曲面通过约束点时,内插曲面也与自由曲线一样非常有用。另一方面,由控制点创建的曲面对于各种平滑级别的探索式设计更为有用。
要创建插值曲面,只需生成与曲面形状近似的点的二维集合即可。集合必须是矩形,即不能出现锯齿。NurbsSurface.ByPoints 方法通过这些点构造曲面。
// python_points_1 is a set of Points generated with
// a Python script found in Chapter 12, Section 10
surf = NurbsSurface.ByPoints(python_points_1);也可以通过指定曲面的基本控制点来创建自由形式的 NurbsSurfaces。与 NurbsCurves 一样,控制点可以看作是表示具有直线段的四边形网格,这可以平滑到最终的曲面形式(取决于曲面的阶数)。要通过控制点创建 NurbsSurface,请为 NurbsSurface.ByPoints 添加两个附加参数,以指示基本曲线在曲面两个方向上的角度。
// python_points_1 is a set of Points generated with
// a Python script found in Chapter 12, Section 10
// create a surface of degree 2 with smooth segments
surf = NurbsSurface.ByPoints(python_points_1, 2, 2);我们可以增加 NurbsSurface 的阶数,来更改生成的曲面几何图形:
// python_points_1 is a set of Points generated with
// a Python script found in Chapter 12, Section 10
// create a surface of degree 6
surf = NurbsSurface.ByPoints(python_points_1, 6, 6);就像可以通过在一组输入点之间内插来创建曲面一样,可以通过在一组基础曲线之间内插来创建曲面。这称为“放样”。放样曲线是使用 Surface.ByLoft 构造函数创建的,其中输入曲线集合作为唯一参数。
// python_points_2, 3, and 4 are generated with
// Python scripts found in Chapter 12, Section 10
c1 = NurbsCurve.ByPoints(python_points_2);
c2 = NurbsCurve.ByPoints(python_points_3);
c3 = NurbsCurve.ByPoints(python_points_4);
loft = Surface.ByLoft([c1, c2, c3]);旋转曲面是通过绕中心轴扫掠基础曲线创建的附加类型的曲面。如果插值曲面是对插值曲线的二维模拟,则旋转曲面是对圆和圆弧的二维模拟。
旋转曲面由基本曲线指定,表示曲面的“边”;轴原点、曲面的基点;轴方向、中心“核心”方向;扫掠开始角;以及扫掠结束角。这些曲面用作 Surface.Revolve 构造函数的输入。
pts = {};
pts[0] = Point.ByCoordinates(4, 0, 0);
pts[1] = Point.ByCoordinates(3, 0, 1);
pts[2] = Point.ByCoordinates(4, 0, 2);
pts[3] = Point.ByCoordinates(4, 0, 3);
pts[4] = Point.ByCoordinates(4, 0, 4);
pts[5] = Point.ByCoordinates(5, 0, 5);
pts[6] = Point.ByCoordinates(4, 0, 6);
pts[7] = Point.ByCoordinates(4, 0, 7);
crv = NurbsCurve.ByPoints(pts);
axis_origin = Point.ByCoordinates(0, 0, 0);
axis = Vector.ByCoordinates(0, 0, 1);
surf = Surface.ByRevolve(crv, axis_origin, axis, 0,
360);通过在三维空间中明确指出 x、y 和 z 坐标,可以创建特定的几何体对象。但是,通常在对象本身或其基本 CoordinateSystem 上使用几何变换将几何体移动到其最终位置。
最简单的几何变换是平移,可在 x、y 和 z 方向上将对象移动指定的单位数。
// create a point at x = 1, y = 2, z = 3
p = Point.ByCoordinates(1, 2, 3);
// translate the point 10 units in the x direction,
// -20 in y, and 50 in z
// p2’s new position is x = 11, y = -18, z = 53
p2 = p.Translate(10, -20, 50);虽然 Dynamo 中的所有对象均可通过在对象名称末尾附加 .Translate 方法进行转换,但更复杂的变换需要将对象从一个基础坐标系变换到新坐标系。例如,要绕 x 轴将对象旋转 45 度,我们将对象从其现有 CoordinateSystem(不旋转)变换为 CoordinateSystem(已使用 .Transform 方法绕 x 轴旋转 45 度):
cube = Cuboid.ByLengths(CoordinateSystem.Identity(),
10, 10, 10);
new_cs = CoordinateSystem.Identity();
new_cs2 = new_cs.Rotate(Point.ByCoordinates(0, 0),
Vector.ByCoordinates(1,0,0.5), 25);
// get the existing coordinate system of the cube
old_cs = CoordinateSystem.Identity();
cube2 = cube.Transform(old_cs, new_cs2);除了平移和旋转外,还可以缩放或剪切 CoordinateSystems。可以使用 .Scale 方法缩放 CoordinateSystem:
cube = Cuboid.ByLengths(CoordinateSystem.Identity(),
10, 10, 10);
new_cs = CoordinateSystem.Identity();
new_cs2 = new_cs.Scale(20);
old_cs = CoordinateSystem.Identity();
cube2 = cube.Transform(old_cs, new_cs2);通过将非正交向量输入 CoordinateSystem 构造函数,可以创建剪切的 CoordinateSystem。
new_cs = CoordinateSystem.ByOriginVectors(
Point.ByCoordinates(0, 0, 0),
Vector.ByCoordinates(-1, -1, 1),
Vector.ByCoordinates(-0.4, 0, 0));
old_cs = CoordinateSystem.Identity();
cube = Cuboid.ByLengths(CoordinateSystem.Identity(),
5, 5, 5);
new_curves = cube.Transform(old_cs, new_cs);缩放和剪切是比旋转和平移更复杂的几何变换,因此并非每个 Dynamo 对象都能进行这些变换。下表概述了 Dynamo 对象可以具有非统一比例缩放的 CoordinateSystems 和剪切的 CoordinateSystems。
弧
否
否
NurbsCurve
是
是
Nurbs 曲面
否
否
圆
否
否
直线
是
是
平面
否
否
点
是
是
多边形
否
否
实体
否
否
曲面
否
否
文本
否
否
代码块是深入 Dynamo 的核心编程语言 DesignScript 的窗口。从头进行构建的 DesignScript 可支持探索式设计工作流,它是一种可读且简明的语言,既可提供对少量代码的即时反馈,也可扩展到大型和复杂交互。DesignScript 还构成引擎的支柱,该引擎推动 Dynamo 的大部分方面“处于底层”。由于在 Dynamo 节点和交互中找到的几乎所有功能都与脚本语言有一对一关系,因此有独特的机会在基于节点的交互和脚本之间以流畅的方式进行移动。
对于初学者,节点可以自动转换为文本语法以帮助学习 DesignScript,或者只是缩小图形较大部分的大小。这是使用名为“节点到代码”过程完成的,将在“DesignScript 语法”部分中详细介绍该过程。有经验的用户可以使用“代码块”创建现有功能的自定义映射,并使用许多标准编码范例来创建用户编写的关系。在初学者和高级用户之间,有大量可加快设计速度的快捷方式和代码段。虽然对于非程序员来说,术语“代码块”可能有点令人畏惧,但它既易于使用又功能强大。初学者可以高效地使用代码块(最少编码),高级用户可以定义脚本化定义以在 Dynamo 定义中的其他位置重新调用。
简而言之,代码块是可视化脚本环境中的文本脚本界面。它们可以用作数字、字符串、公式和其他数据类型。代码块专为 Dynamo 设计,因此用户可以在代码块中定义任意变量,这些变量会自动添加到节点的输入:
使用代码块,用户可以灵活地确定如何指定输入。以下是通过坐标 “(10, 5, 0)” 创建基点的几种不同方法:
当您了解库中更多的可用函数时,您甚至会发现键入“Point.ByCoordinates”比在库中搜索和查找正确的节点更快。例如,当键入 “Point.” 时,Dynamo 会显示可能应用于点的函数列表。这使脚本更加直观,有助于了解如何在 Dynamo 中应用函数。
代码块位于 “核心”>“输入”>“操作”>“代码块” 中。但更快的是,只需双击画布,代码块即会显示。此节点经常被使用,因此赋予其完全双击权限。
代码块在数据类型方面也很灵活。用户可以快速定义数字、字符串和公式,且代码块将提供所需的输出。
在下图中,您可以看到“旧学校”的操作方法有点长:用户在界面中搜索预期节点、将节点添加到画布,然后输入数据。使用代码块,用户可以双击画布来调出节点,然后使用基本语法键入正确的数据类型。
数字和字符串节点是两个 Dynamo 节点示例,这些节点相较于代码块无疑是过时的。
“过去”
代码块
代码块是 Dynamo 中的一项独特功能,可将可视化编程环境与基于文本的环境动态链接。代码块可以访问所有 Dynamo 节点,并且可以在一个节点中定义整个图形。请仔细阅读本章,因为代码块是 Dynamo 的基本构建块。
使用 Dynamo 2.0,我们可以指定在首次打开 Python 窗口时要使用的默认模板 (.py extension)。这是一个渴望已久的请求,因为这可加快 Dynamo 内 Python 的使用。通过使用模板,我们可以在计划开发自定义 Python 脚本时准备好默认导入。
此模板的位置位于 Dynamo 安装的 APPDATA 位置。
这通常如下所示:( %appdata%\Dynamo\Dynamo Core\{version}\ )。
为了能够利用此功能,我们需要在 DynamoSettings.xml 文件中添加以下行。(在记事本中编辑)
在我们看到 <PythonTemplateFilePath /> 的位置,只需将其替换为以下内容:
注意:将 CURRENTUSER 替换为您的用户名
接下来,我们需要使用要内置的功能构建模板。在本例中,我们在使用 Revit 时嵌入 Revit 相关的导入和一些其他典型项目。
您可以开始一个空白记事本文档,并在其中粘贴以下代码:
完成后,将此文件在 APPDATA 位置中另存为 PythonTemplate.py。
定义了 Python 模板后,在每次放置 Python 节点时,Dynamo 都会查找该模板。如果找不到,它将看起来像默认的 Python 窗口。
如果找到 Python 模板(如我们的 Revit 模板),则您会看到所有内置的默认项目。
有关此出色附加功能(由 Radu Gidei 提供)的其他信息可以在此处找到。https://github.com/DynamoDS/Dynamo/pull/8122
尽管 Dynamo 能够创建各种复杂的几何形状,但简单的几何基本体构成任何计算设计的支柱:直接以最终设计形式表示或用作生成更复杂几何体的脚手架。
虽然不是严格的一块几何体,但 CoordinateSystem 是构建几何体的重要工具。CoordinateSystem 对象可记录位置和几何变换(如旋转、调节和缩放)。
以 x = 0、y = 0、z = 0 的点为中心创建 CoordinateSystem,不进行旋转、缩放或调节变换,只需调用 Identity 构造函数:
具有几何变换的 CoordinateSystems 超出本章的范围,但另一个构造函数允许您在特定点 CoordinateSystem.ByOriginVectors 创建坐标系:
最简单的几何基本体是一个点,表示三维空间中的零维位置。如前所述,可以通过几种不同的方式在特定坐标系中创建点:Point.ByCoordinates 使用指定的 x、y 和 z 坐标创建点;Point.ByCartesianCoordinates 使用指定的 x、y 和 z 坐标在特定坐标系中创建点;Point.ByCylindricalCoordinates 使用半径、旋转角度和高度创建位于圆柱体上的点;Point.BySphericalCoordinates 使用半径和两个旋转角度创建位于球体上的点。
本例说明在各种坐标系中创建的点:
下一个较高维度的 Dynamo 基本体是一条线段,表示两个端点之间的无限多个点。可以通过构造函数 Line.ByStartPointEndPoint 明确指定两个边界点,或者通过 Line.ByStartPointDirectionLength 在该方向指定起点、方向和长度来创建直线。
Dynamo 有表示三维中大多数基本类型的几何基本体的对象:立方体,使用 Cuboid.ByLengths 创建;圆锥体,使用 Cone.ByPointsRadius 和 Cone.ByPointsRadii 创建;圆柱体,使用 Cylinder.ByRadiusHeight 创建;球体,使用 Sphere.ByCenterPointRadius 创建。
<PythonTemplateFilePath>
<string>C:\Users\CURRENTUSER\AppData\Roaming\Dynamo\Dynamo Core\2.0\PythonTemplate.py</string>
</PythonTemplateFilePath>import clr
clr.AddReference('RevitAPI')
from Autodesk.Revit.DB import *
from Autodesk.Revit.DB.Structure import *
clr.AddReference('RevitAPIUI')
from Autodesk.Revit.UI import *
clr.AddReference('System')
from System.Collections.Generic import List
clr.AddReference('RevitNodes')
import Revit
clr.ImportExtensions(Revit.GeometryConversion)
clr.ImportExtensions(Revit.Elements)
clr.AddReference('RevitServices')
import RevitServices
from RevitServices.Persistence import DocumentManager
from RevitServices.Transactions import TransactionManager
doc = DocumentManager.Instance.CurrentDBDocument
uidoc=DocumentManager.Instance.CurrentUIApplication.ActiveUIDocument
#Preparing input from dynamo to revit
element = UnwrapElement(IN[0])
#Do some action in a Transaction
TransactionManager.Instance.EnsureInTransaction(doc)
TransactionManager.Instance.TransactionTaskDone()
OUT = element



// create a CoordinateSystem at x = 0, y = 0, z = 0,
// no rotations, scaling, or sheering transformations
cs = CoordinateSystem.Identity();// create a CoordinateSystem at a specific location,
// no rotations, scaling, or sheering transformations
x_pos = 3.6;
y_pos = 9.4;
z_pos = 13.0;
origin = Point.ByCoordinates(x_pos, y_pos, z_pos);
identity = CoordinateSystem.Identity();
cs = CoordinateSystem.ByOriginVectors(origin,
identity.XAxis, identity.YAxis, identity.ZAxis);// create a point with x, y, and z coordinates
x_pos = 1;
y_pos = 2;
z_pos = 3;
pCoord = Point.ByCoordinates(x_pos, y_pos, z_pos);
// create a point in a specific coordinate system
cs = CoordinateSystem.Identity();
pCoordSystem = Point.ByCartesianCoordinates(cs, x_pos,
y_pos, z_pos);
// create a point on a cylinder with the following
// radius and height
radius = 5;
height = 15;
theta = 75.5;
pCyl = Point.ByCylindricalCoordinates(cs, radius, theta,
height);
// create a point on a sphere with radius and two angles
phi = 120.3;
pSphere = Point.BySphericalCoordinates(cs, radius,
theta, phi);p1 = Point.ByCoordinates(-2, -5, -10);
p2 = Point.ByCoordinates(6, 8, 10);
// a line segment between two points
l2pts = Line.ByStartPointEndPoint(p1, p2);
// a line segment at p1 in direction 1, 1, 1 with
// length 10
lDir = Line.ByStartPointDirectionLength(p1,
Vector.ByCoordinates(1, 1, 1), 10);// create a cuboid with specified lengths
cs = CoordinateSystem.Identity();
cub = Cuboid.ByLengths(cs, 5, 15, 2);
// create several cones
p1 = Point.ByCoordinates(0, 0, 10);
p2 = Point.ByCoordinates(0, 0, 20);
p3 = Point.ByCoordinates(0, 0, 30);
cone1 = Cone.ByPointsRadii(p1, p2, 10, 6);
cone2 = Cone.ByPointsRadii(p2, p3, 6, 0);
// make a cylinder
cylCS = cs.Translate(10, 0, 0);
cyl = Cylinder.ByRadiusHeight(cylCS, 3, 10);
// make a sphere
centerP = Point.ByCoordinates(-10, -10, 0);
sph = Sphere.ByCenterPointRadius(centerP, 5);
























在计算设计中,曲线和曲面经常用作基础脚手架,用于构建后续几何体。为了使早期几何体用作以后几何体的基础,脚本必须能够提取诸如整个对象区域的位置和方向等特性。曲线和曲面均支持此提取,并且称为参数化。
曲线上的所有点可以看作具有从 0 到 1 的唯一参数。如果基于多个控制点或插值点创建 NurbsCurve,则第一个点将具有参数 0,而最后一个点将具有参数 1。无法提前知道什么是精确参数以及什么是中间点,这听起来像是严重限制,但这可通过一系列实用程序函数来减轻。虽然使用两个参数而不是一个参数(称为 u 和 v),但曲面的参数化与曲线相似。如果我们要使用以下点创建一个曲面:
pts = [ [p1, p2, p3],
[p4, p5, p6],
[p7, p8, p9] ];p1 将具有参数 u = 0 v = 0,而 p9 将具有参数 u = 1 v = 1。
在确定用于生成曲线的点时,参数化并非特别有用,其主要用途是确定 NurbsCurve 和 NurbsSurface 构造函数生成中间点时的位置。
曲线具有 PointAtParameter 方法,该方法采用 0 到 1 之间的单个双精度参数,并返回该参数处的“点”对象。例如,此脚本会在参数 0、.1、.2、.3、.4、.5、.6、.7、.8、.9 和 1 处查找点:
pts = {};
pts[0] = Point.ByCoordinates(4, 0, 0);
pts[1] = Point.ByCoordinates(6, 0, 1);
pts[2] = Point.ByCoordinates(4, 0, 2);
pts[3] = Point.ByCoordinates(4, 0, 3);
pts[4] = Point.ByCoordinates(4, 0, 4);
pts[5] = Point.ByCoordinates(3, 0, 5);
pts[6] = Point.ByCoordinates(4, 0, 6);
crv = NurbsCurve.ByPoints(pts);
pts_at_param = crv.PointAtParameter(0..1..#11);
// draw Lines to help visualize the points
lines = Line.ByStartPointEndPoint(pts_at_param,
Point.ByCoordinates(4, 6, 0));同样,曲面具有 PointAtParameter 方法,该方法采用两个参数,即生成点的 u 和 v 参数。
尽管提取曲线和曲面上的各个点非常有用,但脚本通常需要了解参数处的特定几何特征,例如曲线或曲面面对的方向。CoordinateSystemAtParameter 方法不仅可以查找位置,还能查找位于曲线或曲面参数处的定向 CoordinateSystem。例如,以下脚本沿旋转曲面提取定向 CoordinateSystems,并使用 CoordinateSystems 的方向生成将法线粘滞到曲面的线:
pts = {};
pts[0] = Point.ByCoordinates(4, 0, 0);
pts[1] = Point.ByCoordinates(3, 0, 1);
pts[2] = Point.ByCoordinates(4, 0, 2);
pts[3] = Point.ByCoordinates(4, 0, 3);
pts[4] = Point.ByCoordinates(4, 0, 4);
pts[5] = Point.ByCoordinates(5, 0, 5);
pts[6] = Point.ByCoordinates(4, 0, 6);
pts[7] = Point.ByCoordinates(4, 0, 7);
crv = NurbsCurve.ByPoints(pts);
axis_origin = Point.ByCoordinates(0, 0, 0);
axis = Vector.ByCoordinates(0, 0, 1);
surf = Surface.ByRevolve(crv, axis_origin, axis, 90,
140);
cs_array = surf.CoordinateSystemAtParameter(
(0..1..#7)<1>, (0..1..#7)<2>);
def make_line(cs : CoordinateSystem) {
lines_start = cs.Origin;
lines_end = cs.Origin.Translate(cs.ZAxis, -0.75);
return = Line.ByStartPointEndPoint(lines_start,
lines_end);
}
lines = make_line(Flatten(cs_array));如前所述,参数化在曲线或曲面的长度上并非始终统一,这意味着参数 0.5 并不始终与中点对应,0.25 并不始终对应于曲线或曲面上的 1/4 点。为了解决此限制,曲线还有一组附加的参数化命令,使您可以沿曲线找到特定长度处的点。
以下 Python 脚本为几个示例生成点数组。应将它们粘贴到 Python 脚本节点,如下所示:
python_points_1
out_points = []
for i in range(11):
sub_points = []
for j in range(11):
z = 0
if (i == 5 and j == 5):
z = 1
elif (i == 8 and j == 2):
z = 1
sub_points.append(Point.ByCoordinates(i, j, z))
out_points.append(sub_points)
OUT = out_pointspython_points_2
out_points = []
for i in range(11):
z = 0
if (i == 2):
z = 1
out_points.append(Point.ByCoordinates(i, 0, z))
OUT = out_pointspython_points_3
out_points = []
for i in range(11):
z = 0
if (i == 7):
z = -1
out_points.append(Point.ByCoordinates(i, 5, z))
OUT = out_pointspython_points_4
out_points = []
for i in range(11):
z = 0
if (i == 5):
z = 1
out_points.append(Point.ByCoordinates(i, 10, z))
OUT = out_pointspython_points_5
out_points = []
for i in range(11):
sub_points = []
for j in range(11):
z = 0
if (i == 1 and j == 1):
z = 2
elif (i == 8 and j == 1):
z = 2
elif (i == 2 and j == 6):
z = 2
sub_points.append(Point.ByCoordinates(i, j, z))
out_points.append(sub_points)
OUT = out_pointsIntersect、Trim 和 SelectTrim 主要用于较低维度的几何图形,例如点、曲线和曲面。另一方面,实体几何图形还有一组附加方法用于在构造后修改形状,方法是以与 Trim 类似的方式减去材质,并将图元合并到一起以形成更大的整体。
Union 方法可获取两个实体对象,并在两个对象覆盖的空间之外创建单个实体对象。对象之间的重叠空间会合并为最终形式。本例将球体和立方体合并为单个实体球体-立方体形状:
s1 = Sphere.ByCenterPointRadius(
CoordinateSystem.Identity().Origin, 6);
s2 = Sphere.ByCenterPointRadius(
CoordinateSystem.Identity().Origin.Translate(4, 0,
0), 6);
combined = s1.Union(s2);Difference 方法类似 Trim,从基础实体中减去输入工具实体的内容。在本例中,我们从球体中穿凿出一个小凹穴:
s = Sphere.ByCenterPointRadius(
CoordinateSystem.Identity().Origin, 6);
tool = Sphere.ByCenterPointRadius(
CoordinateSystem.Identity().Origin.Translate(10, 0,
0), 6);
result = s.Difference(tool);Intersect 方法会返回两个实体输入之间的重叠实体。在以下示例中,Difference 已更改为 Intersect,并且生成的实体是最初穿凿的缺失空心:
s = Sphere.ByCenterPointRadius(
CoordinateSystem.Identity().Origin, 6);
tool = Sphere.ByCenterPointRadius(
CoordinateSystem.Identity().Origin.Translate(10, 0,
0), 6);
result = s.Intersect(tool);计算设计中的对象很少在最终位置和形状中显式创建,并且通常基于现有几何体进行转换、旋转和定位。向量数学作为一种几何脚手架,用于提供几何体的方位和方向,以及概念化通过三维空间的移动而不是直观表示。
最基本的是,向量表示三维空间中的位置,通常被视为从位置 (0, 0, 0) 到该位置的箭头端点。可以使用 ByCoordinates 构造函数创建向量,以获取新创建的向量对象的 x、y 和 z 位置。请注意,向量对象不是几何对象,不会显示在 Dynamo 窗口中。但是,有关新创建或修改的向量的信息可以在控制台窗口中打印:
在向量对象上定义一组数学运算,可允许您在三维空间中添加、减去、相乘和移动对象,就像在数字行上的一维空间中移动实数一样。
向量相加定义为两个向量的分量之和,如果两个分量向量箭头按“尖端到尾部”放置,则可以将向量相加定义为结果向量。向量相加是使用 Add 方法执行的,并由左侧的图表表示。
同样地,可以使用 Subtract 方法将两个向量对象相互减去。可以将向量相减看作从第一个向量到第二个向量的方向。
向量相乘可以看作是按给定比例因子在向量自身方向移动向量的端点。
在缩放向量时,通常需要使结果向量的长度与缩放量完全相等。通过首先标准化向量,即将向量的长度精确设置为一,可以轻松实现该目的。
c 仍指向与 a (1, 2, 3) 相同的方向,虽然现在它的长度完全等于 5。
向量数学中还存在两种其他方法,它们与一维数学、矢积和点积不完全平行。矢积是生成向量的一种方法,该向量(在 90 度)与两个现有向量正交。例如,x 轴和 y 轴的矢积为 z 轴,尽管这两个输入向量不需要相互正交。使用 Cross 方法计算矢积向量。
另外,某些向量数学的更高级函数是点积。两个向量之间的点积是一个实数(不是向量对象),它与两个向量之间的角度相关,但并不完全相关。点积的一个有用属性是:仅当两个向量垂直时,它们之间的点积将为 0。点积使用 Dot 方法计算。
// construct a Vector object
v = Vector.ByCoordinates(1, 2, 3);
s = v.X + " " + v.Y + " " + v.Z;a = Vector.ByCoordinates(5, 5, 0);
b = Vector.ByCoordinates(4, 1, 0);
// c has value x = 9, y = 6, z = 0
c = a.Add(b);a = Vector.ByCoordinates(5, 5, 0);
b = Vector.ByCoordinates(4, 1, 0);
// c has value x = 1, y = 4, z = 0
c = a.Subtract(b);a = Vector.ByCoordinates(4, 4, 0);
// c has value x = 20, y = 20, z = 0
c = a.Scale(5);a = Vector.ByCoordinates(1, 2, 3);
a_len = a.Length;
// set the a's length equal to 1.0
b = a.Normalized();
c = b.Scale(5);
// len is equal to 5
len = c.Length;a = Vector.ByCoordinates(1, 0, 1);
b = Vector.ByCoordinates(0, 1, 1);
// c has value x = -1, y = -1, z = 1
c = a.Cross(b);a = Vector.ByCoordinates(1, 2, 1);
b = Vector.ByCoordinates(5, -8, 4);
// d has value -7
d = a.Dot(b);













简单来说,代码块中有一些基本的简写方法,这些方法使数据管理 更加 容易。我们将详细介绍下面的基础知识,并讨论如何使用此简写来创建和查询数据。
数据类型
标准 Dynamo
等效代码块
编号
字符串
序列
范围
获取索引处的项目
创建列表
连接字符串
条件语句
节点
等效代码块
注释
任何运算符(+、&&、>=、Not 等)
+、&&、>=、! 等
请注意,“Not”变为“!”,但该节点被称为“Not”以区分“阶乘”
布尔值 True
true;
注意小写
布尔值 False
false;
注意小写
定义范围和序列的方法可缩减为基本简写。使用下图作为“..”语法的指导,以使用代码块定义数值数据列表。在完成此标记法后,创建数值数据是一个非常有效的过程:
在本例中,数字范围会替换为定义
beginning..end..step-size;的基本 “代码块” 语法。通过以数字表示,可以得到:0..10..1;请注意,语法
0..10..1;等同于0..10;,步长 1 是简写表示法的默认值。因此,0..10;将给出一个从 0 到 10 的序列(步长为 1)。“序列” 示例类似,除了我们使用“#”来指明我们希望列表中包含 15 个值,而不是列表中的最大值为 15。在本例中,我们将定义:
beginning..#ofSteps..step-size:。序列的实际语法为0..#15..2使用上一步中的 “#”,我们现在将其放置在语法的 “step-size” 部分中。现在,我们有一个 数字范围,从 “beginning” 到 “end”,“step-size” 表示法指示两者之间均匀分布多个值:
beginning..end..#ofSteps
创建高级范围后,我们即可简单地处理列表的列表。在下面的示例中,我们将隔离主要范围表示法的变量,并创建该列表的另一个范围。
1.创建嵌套范围,将带“#”的表示法与不带符号的表示法进行比较。相同逻辑在基本范围中都适用,但它稍显复杂。
2.我们可以在主范围内的任意位置处定义子范围;请注意,我们也可以有两个子范围。
3.通过控制范围中的“end”值,我们可以创建多个长度不同的的范围。
作为逻辑练习,请比较上述两个简写,并尝试解析 “subranges” 和 “#” 表示法如何驱动结果输出。
除了使用简写生成列表外,我们还可以即时创建列表。这些列表可以包含多种元素类型,也可以进行查询(请记住,列表本身就是对象)。总之,使用代码块时,可以创建列表,并从带括号的列表中查询项目(即“方括号”):
1.使用字符串快速创建列表,并使用项目索引进行查询。
2.使用变量创建列表,并使用范围简写表示法进行查询。
管理嵌套列表的过程类似。请注意列表顺序,并使用多组方括号进行调用:
1.定义一列列表。
2.使用单括号表示法查询列表。
3.使用双括号表示法查询项目。
单击下面的链接下载示例文件。
可以在附录中找到示例文件的完整列表。
在本练习中,我们将调整新的简写技能,以创建由范围和公式定义的精美蛋壳曲面。在本练习中,请注意我们如何串联使用代码块和现有 Dynamo 节点:我们将代码块用于繁重的数据提升,而 Dynamo 节点以可视方式布局来使定义清晰易读。
首先,通过连接上述节点创建曲面。请勿使用数字节点定义宽度和长度,而是双击画布并在代码块中键入 100;
通过在 “代码块” 中键入
0..1..#50,定义一个介于 0 和 1 之间的范围(其中包含 50 个划分)。将该范围连接到 “Surface.PointAtParameter”,它会在曲面上提取介于 0 和 1 之间的 u 和 v 值。请记得通过在 “Surface.PointAtParameter” 节点上单击鼠标右键,将“连缀”更改为“叉积”。
在此步骤中,我们会使用第一个函数以在 Z 方向上向上移动点栅格。此栅格将基于底层函数驱动生成的曲面。添加新节点,如下图所示
我们使用带有以下行的码块:
(0..Math.Sin(x*360)..#50)*5;。为了对该内容快速详细介绍,我们定义了一个内部带有公式的范围。此公式为正弦函数。在 Dynamo 中,正弦函数接收度数输入,因此为了获得完整正弦波,我们将x 值(这一范围输入介于 0 到 1 之间)乘以 360。接下来,我们希望每行都具有与控制栅格点相同数量的划分,因此我们用 #50 定义了 50 个细分。最后,乘数 5 只会增加平移幅度,因此我们可以在 Dynamo 预览中查看效果。
虽然上一个 “代码块” 正常工作,但它并非完全参数化。我们希望动态驱动其参数,因此我们会将上一步中的代码行替换为
(0..Math.Sin(x*360*cycles)..#List.Count(x))*amp;。这使我们可以根据输入定义这些值。
通过更改滑块(范围介于 0 到 10 之间),我们会得到一些有趣的结果。
通过对数字范围执行转置,我们会反转幕墙波的方向:
transposeList = List.Transpose(sineList);
如果添加 sineList 和 tranposeList,我们会得到一个扭曲的蛋壳曲面:
eggShellList = sineList+transposeList;
我们会更改下面指定的滑块值,以“平静地控制”该算法。
最后,让我们使用“代码块”查询数据的隔离部分。要使用特定范围的点重新生成曲面,请在 “Geometry.Translate” 和 “NurbsSurface.ByPoints” 节点之间添加上述代码块。这包含以下文本行:sineStrips[0..15..1];。这将选择前 16 行点(共 50 行)。通过重新创建曲面,可以看到我们已生成点栅格的隔离部分。
在最后一步中,为了提高此 “代码块” 的参数化,我们使用介于 0 到 1 之间的滑块来驱动查询。我们使用以下代码行来执行此操作:
sineStrips[0..((List.Count(sineStrips)-1)*u)];。这看起来可能会令人困惑,但代码行提供的方法让我们可以快速地将列表的长度缩放为 0 到 1 之间的乘数。
滑块上的值 0.53 会创建刚好经过栅格中点的曲面。
如预期的一样,值为 1 的滑块会基于完整的栅格点创建曲面。
通过查看可视图形,我们可以亮显代码块并查看其每个函数。
1.第一个 “代码块” 替换 “Number” 节点。
2.第二个 “代码块” 替换 “Number Range” 节点。
3.第三个代码块替换 List.Transpose、List.Count 和 Number Range 节点。
4.第四个 “代码块” 查询一列列表,以便替换 “List.GetItemAtIndex” 节点。
在 Dynamo 中,有两种基本方法可以创建自由形式的曲线:指定点集合,并使 Dynamo 在它们之间内插平滑曲线,或者通过指定一定阶数曲线的基本控制点的更低级别方法。当设计师确切知道线应采用的形状,或者设计是否具有特定约束来控制曲线可以和不能通过的位置时,插值曲线非常有用。通过控制点指定的曲线本质上是一系列直线线段,算法会将其平滑为最终曲线形式。对于通过不同平滑度探索曲线形状或在需要曲线段之间的平滑连续性时,通过控制点指定曲线非常有用。
要创建插值曲线,只需将点集合传递到 NurbsCurve.ByPoints 方法。
生成的曲线与每个输入点相交,分别在集合中的第一个点和最后一个点开始和结束。可以使用可选的周期性参数创建闭合的周期性曲线。Dynamo 将自动填充缺失的段,因此不需要重复的端点(与起点相同)。
NurbsCurves 的生成方式几乎相同,输入点表示直线段的端点,第二个参数用于指定曲线经历的平滑量和类型(称为阶数)。* 阶数为 1 的曲线没有平滑;它是多段线。
对阶数为 2 的曲线进行平滑处理,使曲线相交并与多段线线段的中点相切:
Dynamo 支持最多 20 阶的 NURBS(非均匀有理 B 样条曲线)曲线,以下脚本说明了增加平滑级别对曲线形状的影响:
请注意,必须至少比曲线阶数多一个控制点。
通过控制点构建曲线的另一个好处是能够保持各个曲线段之间的相切。通过提取最后两个控制点之间的方向,并继续使用以下曲线的前两个控制点来完成此操作。下例创建两条单独的 NURBS 曲线,它们仍然像一条曲线一样平滑:
num_pts = 6;
s = Math.Sin(0..360..#num_pts) * 4;
pts = Point.ByCoordinates(1..30..#num_pts, s, 0);
int_curve = NurbsCurve.ByPoints(pts);pts = Point.ByCoordinates(Math.Cos(0..350..#10),
Math.Sin(0..350..#10), 0);
// create an closed curve
crv = NurbsCurve.ByPoints(pts, true);
// the same curve, if left open:
crv2 = NurbsCurve.ByPoints(pts.Translate(5, 0, 0),
false);num_pts = 6;
pts = Point.ByCoordinates(1..30..#num_pts,
Math.Sin(0..360..#num_pts) * 4, 0);
// a B-Spline curve with degree 1 is a polyline
ctrl_curve = NurbsCurve.ByControlPoints(pts, 1);num_pts = 6;
pts = Point.ByCoordinates(1..30..#num_pts,
Math.Sin(0..360..#num_pts) * 4, 0);
// a B-Spline curve with degree 2 is smooth
ctrl_curve = NurbsCurve.ByControlPoints(pts, 2);num_pts = 6;
pts = Point.ByCoordinates(1..30..#num_pts,
Math.Sin(0..360..#num_pts) * 4, 0);
def create_curve(pts : Point[], degree : int)
{
return = NurbsCurve.ByControlPoints(pts,
degree);
}
ctrl_crvs = create_curve(pts, 1..11);pts_1 = {};
pts_1[0] = Point.ByCoordinates(0, 0, 0);
pts_1[1] = Point.ByCoordinates(1, 1, 0);
pts_1[2] = Point.ByCoordinates(5, 0.2, 0);
pts_1[3] = Point.ByCoordinates(9, -3, 0);
pts_1[4] = Point.ByCoordinates(11, 2, 0);
crv_1 = NurbsCurve.ByControlPoints(pts_1, 3);
pts_2 = {};
pts_2[0] = pts_1[4];
end_dir = pts_1[4].Subtract(pts_1[3].AsVector());
pts_2[1] = Point.ByCoordinates(pts_2[0].X + end_dir.X,
pts_2[0].Y + end_dir.Y, pts_2[0].Z + end_dir.Z);
pts_2[2] = Point.ByCoordinates(15, 1, 0);
pts_2[3] = Point.ByCoordinates(18, -2, 0);
pts_2[4] = Point.ByCoordinates(21, 0.5, 0);
crv_2 = NurbsCurve.ByControlPoints(pts_2, 3);






































您可能已注意到 Dynamo 中节点名称的共同主题:每个节点都使用不带空格的 “.” 语法。这是因为每个节点顶部的文本表示脚本编写的实际语法,而 “.” (或 点符号 )会将图元与我们可以调用的可能方法分开。这样可轻松地将可视化脚本编写转换为基于文本的脚本编写。
作为点符号的一般类比,我们如何在 Dynamo 中处理参数化苹果?以下是我们在决定食用苹果之前将对苹果运行的几种方法。(注意:这些不是实际的 Dynamo 方法):
苹果是什么颜色?
Apple.color
red
苹果成熟了吗?
Apple.isRipe
true
苹果的重量是多少?
Apple.weight
6 oz.
苹果来自哪里?
Apple.parent
树
苹果创建什么?
Apple.children
种子
这个苹果是本地种植的吗?
Apple.distanceFromOrchard
60 mi.
我不了解您,但是从上表中的输出来看,这看起来像一只美味的苹果。我想我会 “Apple.eat()” 它。
考虑到苹果的类比,我们来看一下 “Point.ByCoordinates”,并说明如何使用代码块创建点。
“代码块” 语法 Point.ByCoordinates(0,10); 会提供与 Dynamo 中的 “Point.ByCoordinates” 节点相同的结果,但我们能够使用一个节点创建点。这相较于将单独的节点连接到 “X” 和 Y” 更加高效。
通过在代码块中使用 “Point.ByCoordinates”,我们指定输入的顺序与现成节点 “(X,Y)” 相同。
只要节点不是特殊的 “UI”节点 (具有特殊用户接口功能的节点),即可通过代码块调用库中的任何常规节点。例如,可以调用 “Circle.ByCenterPointRadius”,但调用 “Watch 3D” 节点并没有什么意义。
常规节点(库中的大多数)通常有三种类型。您会发现库是按这些类别来组织的。在代码块内调用这三种类型的方法或节点时,它们的处理方式有所不同。
创建 - 创建(或构造)对象
操作 - 对某个对象执行操作
查询 - 获取已存在对象的特性
“创建”类别将从头开始构造几何图形。我们在代码块中从左到右输入值。这些输入与节点上从上到下的输入顺序相同。
比较 “Line.ByStartPointEndPoint” 节点和代码块中的对应语法,可得到相同的结果。
操作是对该类型的对象所执行的动作。Dynamo 使用许多编码语言通用的 “点符号”,来对某个对象应用操作。确定操作对象后,键入一个点,然后键入操作的名称。与创建类方法一样,操作类方法的输入放置在圆括号中,只是您无需在相应节点上指定所见到的第一个输入。反之,我们指定要对其执行操作的元素:
“Point.Add” 节点是操作类节点,因此语法的工作方式略有不同。
要添加给它的输入分别是 (1) 点 和 (2) 向量。在 “代码块” 中,我们已将点(对象)命名为 “pt”。要将名为*“vec”*的向量添加到 “pt”,我们会写入 “pt.Add(vec)” 或写入“: 对象、点、操作”。“添加”操作仅有一个输入,或 “Point.Add” 节点的所有输入减去第一个输入。“Point.Add” 节点的第一个输入是点本身。
查询类方法可获取对象的特性。由于对象本身是输入,因此不必再指定任何输入。无需输入圆括号。
带节点的连缀与带代码块的连缀略有不同。使用节点,用户在相应节点上单击鼠标右键并选择要执行的连缀选项。使用代码块,用户可以更好地控制如何创建数据的结构。代码块简写方法使用 “复制指南”,来设置多个一维列表应如何成对。尖括号“<>”中的数字定义所生成嵌套列表的层次结构:<1>、<2>、<3> 等。
在本例中,我们使用简写来定义两个范围(更多简写位于本章的以下部分中)。简而言之,
0..1;等效于{0,1},-3..-7等效于{-3,-4,-5,-6,-7}。结果将生成由 2 个 X 值和 5 个 Y 值组成的列表。如果我们不对这些不匹配的列表使用复制指南,则会得到一列两点,即最短列表的长度。使用“复制指南”,我们可以找出 2 和 5 坐标的所有可能组合(或“笛卡尔积”)。使用语法 Point.ByCoordinates
(x_vals<1>,y_vals<2>);,可以得到 两 个列表(其中,每个列表中包含 五 个项目)。使用语法 Point.ByCoordinates
(x_vals<2>,y_vals<1>);,可以得到 五 个列表(其中,每个列表中包含 两 个项目)。
使用这种表示法,我们还可以指定哪个列表是主列表:2 个列表(每个列表 5 个项目)或 5 个列表(每个列表 2 个项目)。在该示例中,更改复制指南的顺序可在栅格中生成以下结果:一个列表(每个列表包含一行点)或一个列表(每个列表包含一列点)。
尽管上面的代码块方法可能需要一些时间来适应,但 Dynamo 中有一个名为“节点到代码”的功能,该功能可使该过程更加容易。要使用此功能,请在 Dynamo 图形中选择一组节点,在画布上单击鼠标右键并选择“节点到代码”。Dynamo 将这些节点以及所有输入和输出压缩到代码块中!这不仅是学习代码块的优秀工具,还支持您更高效地处理参数化 Dynamo 图形。我们将通过使用“节点到代码”来汇总下面的练习,因此不要错过它。
单击下面的链接下载示例文件。
可以在附录中找到示例文件的完整列表。
为了展现代码块的强大功能,我们将把现有的吸引器字段定义转换为代码块形式。使用现有定义演示代码块如何与可视化脚本编写相关联,有助于了解 DesignScript 语法。
首先,在上图中重新创建定义(或打开样例文件)。
请注意,“Point.ByCoordinates” 上的连缀已设置为 “笛卡尔积”。
在 Z 方向上,栅格中的每个点均基于其距参照点的距离进行上移。
曲面将重新创建并加厚,从而在几何图形中相对于距参照点的距离创建隆起。
从头开始,我们先定义参照点:Point.ByCoordinates
(x,y,0);,我们使用与参照点节点顶部指定的相同 “Point.ByCoordinates” 语法。变量 “x” 和 “y” 会插入到 “代码块” 中,以便我们可以使用滑块动态地更新这些变量。
将一些 “滑块” 添加到 “代码块” 输入中(范围介于 -50 到 50 之间)。这样,我们就可以跨越默认的 Dynamo 栅格。
在 “代码块” 的第二行中,我们定义简写来替换数字序列节点:
coordsXY = (-50..50..#11);。我们将在下一节中详细介绍此内容。现在,请注意,此简写与可视化脚本中的 “数字序列” 节点等效。
现在,我们要基于 “coordsXY” 序列创建点栅格。为此,我们要使用 “Point.ByCoordinates” 语法,但还需要采用与在可视化脚本中相同的方式初始化列表的 “笛卡尔积”。为此,我们键入以下代码行:
gridPts = Point.ByCoordinates(coordsXY<1>,coordsXY<2>,0);。尖括号表示叉积参照。请注意,在 “Watch3D” 节点中,我们有一个点栅格穿过 Dynamo 栅格。
现在,面临的棘手情形是:我们希望根据距参照点的距离向上移动点栅格。首先,我们调用这组新的点 “transPts”。由于平移是对现有图元的操作(而不是使用
Geometry.Translate...),因此我们使用gridPts.Translate从画布上的实际节点读取时,我们会看到有三个输入。由于我们要对该图元执行操作(使用 “gridPts.Translate” ),因此已声明要平移的几何图形。其余两个输入将插入到函数的圆括号中:方向和 距离。
方向足够简单,我们使用
Vector.ZAxis()来沿垂直方向移动。参照点与每个栅格点之间的距离仍需进行计算,因此我们以相同方式对该参照点执行这一计算操作:
refPt.DistanceTo(gridPts)代码的最后一行会给出平移后的点:
transPts=gridPts.Translate(Vector.ZAxis(),refPt.DistanceTo(gridPts));
现在,我们得到具有相应数据结构的点栅格来创建 Nurbs 曲面。我们使用
srf = NurbsSurface.ByControlPoints(transPts);构建曲面
最后,要为曲面添加一些深度,我们使用
solid = srf.Thicken(5);构建实体;在本例中,我们使用代码将曲面加厚 5 个单位,但我们始终可以将其声明为一个变量(例如,将它命名为“thickness”),然后使用滑块控制该值。
“节点到代码”功能可自动执行我们只需单击按钮完成的整个练习。这不仅对于创建自定义定义和可重复使用的代码块来说是一项强大的功能,而且它对了解如何在 Dynamo 中编写脚本也是一个非常有用的工具:
从练习的步骤 1 开始处理现有可视化脚本。选择所有节点,在画布上单击鼠标右键,然后选择 “节点到代码”。就这么简单。
Dynamo 具有基于可视化图的版本、连缀和所有功能自动执行的文字。对可视化脚本测试此功能,然后发布该代码块的功能!
函数可以在代码块中创建,并在 Dynamo 定义中的其他位置进行调用。这将在参数化文件中创建另一层控制,并且可以作为基于文本版本的自定义节点进行查看。在这种情况下,“父”代码块可以随时访问,并且可以位于图形上的任意位置。不需要引线!
第一行包含关键字“def”,接着是函数名称,然后是括号中的输入名称。大括号定义函数的主体。使用“return =”返回值。定义函数的代码块没有输入或输出端口,因为它们是从其他代码块调用的。
/*This is a multi-line comment,
which continues for
multiple lines*/
def FunctionName(in1,in2)
{
//This is a comment
sum = in1+in2;
return sum;
};通过提供名称和相同数量的参数,调用同一文件中具有另一代码块的函数。其工作原理与库中现成的节点一样。
FunctionName(in1,in2);单击下面的链接下载示例文件。
可以在附录中找到示例文件的完整列表。
在本练习中,我们将创建一个通用定义,该定义将通过输入的点列表创建球体。这些球体的半径由每个点的 Z 特性驱动。
首先从 0 到 100 的十个值范围开始。将这些值插入到 “Point.ByCoordinates” 节点,以创建对角线。
创建 “代码块”,然后引入我们的定义。
使用以下代码行:
def sphereByZ(inputPt) { };“inputPt” 是我们提供的名称,用于表示将驱动函数的点。目前,该函数不会执行任何操作,但我们会在后续步骤中构建此函数。
通过添加到 “代码块” 函数,我们放置注释和 “sphereRadius” 变量(该变量会查询每个点的 “Z” 位置)。请记住,“inputPt.Z”不需要圆括号用作方法。这是对现有图元特性的“查询”,因此不需要输入:
def sphereByZ(inputPt,radiusRatio) { //get Z Value, ise ot to drive radius of sphere sphereRadius=inputPt.Z; };
现在,我们再次调用在另一个 “代码块” 中创建的函数。如果双击画布以创建新的 “代码块”,然后键入 “sphereB”,我们会注意到 Dynamo 建议使用已定义的 “sphereByZ” 函数。您的函数已添加到智能库!非常棒。
现在,我们调用相应函数并创建一个名为 “Pt” 的变量,以连接在之前步骤中创建的点:
sphereByZ(Pt)在输出中,我们注意到所有值都为空值。这是为什么呢?在定义函数后,我们会计算 “sphereRadius” 变量,但是我们未定义函数应 “返回” 为 “输出” 的内容。可以在下一个步骤中修复该问题。
一个重要步骤是,我们需要定义函数的输出,方法是将一行代码
return = sphereRadius;添加到 “sphereByZ” 函数。现在,我们看到“代码块”的输出为我们提供了每个点的 Z 坐标。
现在,让我们通过编辑 “父” 函数,来创建实际球体。
首先,我们使用代码行定义一个球体:
sphere=Sphere.ByCenterPointRadius(inputPt,sphereRadius);接下来,我们将返回值更改为 “sphere”,而不是 “sphereRadius”:
return = sphere;。这会在 Dynamo 预览中为我们提供一些巨大的球体!
1.要调整这些球体的大小,我们通过添加分隔器来更新“sphereRadius”值:
sphereRadius = inputPt.Z/20;。现在,我们可以看到单独的球体,然后开始了解半径和 Z 值之间的关系。
在 “Point.ByCoordinates” 节点上,通过将连缀从“最短列表”更改为“叉积”,我们将创建点栅格。“sphereByZ” 函数仍然完全有效,因此所有点都会使用基于 Z 值的半径创建球体。
仅是为了测试水域,我们将原始数字列表连接到 “Point.ByCoordinates” 的 X 输入。现在,我们得到了一个球体的立方体。
注意:如果在计算机上的计算需要较长时间,请尝试将 “#10” 更改为 “#5” 之类的值。
请记住,我们创建的 “sphereByZ” 函数是一个通用函数,因此我们可以调用前一课中的螺旋,然后对其应用该函数。
最后一步:使用用户定义的参数来控制半径比。为此,我们需要为该函数创建新输入,并将 “20” 除数替换为参数。
将 “sphereByZ” 定义更新为以下内容:
def sphereByZ(inputPt,radiusRatio) { //get Z Value, use it to drive radius of sphere sphereRadius=inputPt.Z/radiusRatio; //Define Sphere Geometry sphere=Sphere.ByCenterPointRadius(inputPt,sphereRadius); //Define output for function return sphere; };通过向输入中添加“ratio”变量来更新子 “代码块”:
sphereByZ(Pt,ratio);。将滑块插入到新创建的 “代码块” 输入中,并根据半径比改变半径大小。
目前,许多示例都关注从较少维的对象构造较多维的几何体。相交方法允许此较高维度的几何图形生成较低维度的对象,而“修剪”和“选择修剪”命令允许脚本在创建几何形状后对其进行大量修改。
Intersect 方法在 Dynamo 中的所有几何图形上定义,这意味着理论上,任何几何图形都可以与任何其他几何图形相交。通常,由于结果对象将始终是输入点本身,因此某些交点没有意义(例如涉及点的交点)。下图概述了对象之间可能存在的交点组合。下图概述了各种相交操作的结果:
下面非常简单的示例演示了平面与 NurbsSurface 的交集。该交集会生成 NurbsCurve 数组,可像使用任何其他 NurbsCurve 一样使用。
Trim 方法与“Intersect”方法非常相似,因为它几乎为每个几何图形都定义了该方法。但是,与 Intersect 相比,Trim 存在更多限制。
关于 Trim 方法需要注意的是,需要“选择”点、确定要丢弃哪些几何图形的点以及要保留哪些部分。Dynamo 会查找并放弃与选择点最近的已修剪几何图形。































其中:
曲面
曲线
平面
实体
曲面
曲线
点
点,曲线
曲面
曲线
点
点
点
曲线
平面
曲线
点
曲线
曲线
实体
曲面
曲线
曲线
实体
// python_points_5 is a set of Points generated with
// a Python script found in Chapter 12, Section 10
surf = NurbsSurface.ByPoints(python_points_5, 3, 3);
WCS = CoordinateSystem.Identity();
pl = Plane.ByOriginNormal(WCS.Origin.Translate(0, 0,
0.5), WCS.ZAxis);
// intersect surface, generating three closed curves
crvs = surf.Intersect(pl);
crvs_moved = crvs.Translate(0, 0, 10);使用: 点
曲线
平面
曲面
实体
开: 曲线
是
否
否
否
否
多边形
-
否
是
否
否
曲面
-
是
是
是
是
实体
-
-
是
是
是
// python_points_5 is a set of Points generated with
// a Python script found in Chapter 12, Section 10
surf = NurbsSurface.ByPoints(python_points_5, 3, 3);
tool_pts = Point.ByCoordinates((-10..20..10)<1>,
(-10..20..10)<2>, 1);
tool = NurbsSurface.ByPoints(tool_pts);
pick_point = Point.ByCoordinates(8, 1, 3);
result = surf.Trim(tool, pick_point);

现在,我们已演示了如何在 Dynamo 中使用 Python 脚本,接下来我们来了解如何将 Revit 库连接到脚本编写环境。请记住,我们输入了 Python 标准和 Dynamo 核心节点,其中代码块中前四行如下所示。要输入 Revit 节点、Revit 图元和 Revit 文档管理器,我们只需添加几行代码即可:
import sys
import clr
clr.AddReference('ProtoGeometry')
from Autodesk.DesignScript.Geometry import *
# Import RevitNodes
clr.AddReference("RevitNodes")
import Revit
# Import Revit elements
from Revit.Elements import *
# Import DocumentManager
clr.AddReference("RevitServices")
import RevitServices
from RevitServices.Persistence import DocumentManager
import System这样,我们便可以访问 Revit API,并为任何 Revit 任务提供自定义脚本编写。通过将可视化编程流程与 Revit API 脚本编写相结合,协作和工具开发得到显著改进。例如,BIM 经理和方案设计人员可以协同处理同一图形。在此协作中,他们可以改进模型的设计和执行。
Dynamo 项目背后的计划是拓宽平台实施范围。随着 Dynamo 向 Docket 中添加更多程序,用户将可以从 Python 脚本编写环境访问特定于平台的 API。尽管 Revit 是本部分的案例研究,但我们可以预见将来会有更多章节,这些章节会提供有关在其他平台上编写脚本的综合教程。此外,现在还有许多 IronPython 库可供访问,这些库都可以输入到 Dynamo 中!
下面的示例演示了在 Dynamo 中使用 Python 实现特定于 Revit 操作的方法。有关 Python 与 Dynamo 和 Revit 关系的更详细综述,请参见 Dynamo Wiki 页面。Python 和 Revit 的另一个有用资源是 Revit Python Shell 项目。
创建新的 Revit 项目。
单击下面的链接下载示例文件。
可以在附录中找到示例文件的完整列表。
在这些练习中,我们将在 Dynamo for Revit 中了解基本的 Python 脚本。本练习将重点介绍如何处理 Revit 文件和图元,以及 Revit 和 Dynamo 之间的通信。
这是一种用于检索与 Dynamo 任务链接的 Revit 文件的 doc、uiapp 和 app 的简便方法。之前使用过 Revit API 的程序员可能会在观察列表中注意到这些项目。如果这些项目看起来不太熟悉,没关系;我们将在下面练习中使用其他示例。
下面介绍如何在 Dynamo 中输入 Revit 服务和检索文档数据。
在 Dynamo 中查看 Python 节点。还可以在下面找到代码:
# Load the Python Standard and DesignScript Libraries
import sys
import clr
#Import DocumentManager
clr.AddReference("RevitServices")
import RevitServices
from RevitServices.Persistence import DocumentManager
#Place your code below this line
doc = DocumentManager.Instance.CurrentDBDocument
uiapp = DocumentManager.Instance.CurrentUIApplication
app = uiapp.Application
#Assign your output to the OUT variable
OUT = [doc,uiapp,app]单击下面的链接下载示例文件。
可以在附录中找到示例文件的完整列表。
在本练习中,我们将在 Revit 中使用 Dynamo Python 节点创建一个简单的模型曲线。
先在 Revit 中创建新的概念体量族。
打开 “概念体量文件夹”,然后使用 “Metric Mass.rft” 模板文件。
在 Revit 中,使用键盘快捷键 un 显示“项目单位设置”,将长度单位更改为“米”。
启动 Dynamo,然后创建下图中的节点集。首先,我们将在 Revit 中基于 Dynamo 节点创建两个参照点。
创建 “代码块”,并为其赋值
"0;"将该值插入 X、Y 和 Z 输入的 “ReferencePoint.ByCoordinates” 节点。
创建三个滑块,范围从 -100 到 100,步长为 1。
将每个滑块都连接到 “ReferencePoint.ByCoordinates” 节点。
将 “Python” 节点添加到工作空间,单击节点上的“+”按钮以添加另一个输入并将两个参照点插入到每个输入。打开 “Python” 节点。
在 Dynamo 中查看 Python 节点。在下面查找完整代码。
System.Array:Revit 需要 “系统数组” 作为输入(而不是 Python 列表)。这只是多一行代码,但注意参数类型将有助于在 Revit 中进行 Python 编程。
import sys
import clr
# Import RevitNodes
clr.AddReference("RevitNodes")
import Revit
#Import Revit elements
from Revit.Elements import *
import System
#define inputs
startRefPt = IN[0]
endRefPt = IN[1]
#define system array to match with required inputs
refPtArray = System.Array[ReferencePoint]([startRefPt, endRefPt])
#create curve by reference points in Revit
OUT = CurveByPoints.ByReferencePoints(refPtArray)在 Dynamo 中,我们使用 Python 创建了两个参照点以及一条连接它们的线。在下一练习中,我们将进一步介绍。
单击下面的链接下载示例文件。
可以在附录中找到示例文件的完整列表。
本练习尽可能简单,但主要介绍将数据和几何图形从 Revit 连接到 Dynamo 和反向操作的主题。首先,打开 Revit-StructuralFraming.rvt。打开后,启动 Dynamo 并打开“Revit-StructuralFraming.dyn”文件。
此 Revit 文件实现的是基本功能。两条参照曲线:一条在标高 1 上绘制,另一条在标高 2 上绘制。我们希望将这些曲线输入 Dynamo,并保持实时链接。
在此文件中,我们有一组节点插入到 Python 节点的五个输入中。
选择模型图元节点:点击每个节点对应的选择按钮,然后在 Revit 中选择相应曲线。
代码块: 使用语法
0..1..#x;, 将介于 0 到 20 之间的整数滑块连接到 “x” 输入。这会指定要在两条曲线之间绘制的梁数。结构框架类型:此处,我们将从下拉菜单中选择默认的 W12x26 梁。
标高:选择“标高 1”。
Python 中的这段代码更加密集,但代码中的注释描述了该过程中出现的情况
import clr
#import Dynamo Geometry
clr.AddReference('ProtoGeometry')
from Autodesk.DesignScript.Geometry import *
# Import RevitNodes
clr.AddReference("RevitNodes")
import Revit
# Import Revit elements
from Revit.Elements import *
import System
#Query Revit elements and convert them to Dynamo Curves
crvA=IN[0].Curves[0]
crvB=IN[1].Curves[0]
#Define input Parameters
framingType=IN[3]
designLevel=IN[4]
#Define "out" as a list
OUT=[]
for val in IN[2]:
#Define Dynamo Points on each curve
ptA=Curve.PointAtParameter(crvA,val)
ptB=Curve.PointAtParameter(crvB,val)
#Create Dynamo line
beamCrv=Line.ByStartPointEndPoint(ptA,ptB)
#create Revit Element from Dynamo Curves
beam = StructuralFraming.BeamByCurve(beamCrv,designLevel,framingType)
#convert Revit Element into list of Dynamo Surfaces
OUT.append(beam.Faces)在 Revit 中,我们有一组横跨两条曲线的梁作为结构图元。注意:这不是一个真实示例...结构图元用作从 Dynamo 创建的原生 Revit 实例的示例。
在 Dynamo 中,我们也可以看到结果。“Watch3D” 节点中的梁引用从 Revit 图元查询所得的几何图形。
请注意,我们有一个连续过程,将数据从 Revit 环境转换到 Dynamo 环境。总之,下面介绍了该过程的具体流程:
选择 Revit 图元
将 Revit 图元转换为 Dynamo 曲线
将 Dynamo 曲线分割为一系列 Dynamo 点
使用两条曲线之间的 Dynamo 点来创建 Dynamo 线
通过参照 Dynamo 线创建 Revit 梁
通过查询 Revit 梁的几何图形输出 Dynamo 曲面
这听起来可能有点费劲,但脚本使它变得简单,就像在 Revit 中编辑曲线并重新运行求解器一样(尽管这样做可能必须删除以前的梁)。这是因为我们在 Pyhon 中放置梁,从而打破了 OOTB 节点的关联。
在 Revit 中更新参照曲线后,我们会得到新的梁阵列。













为什么要在 Dynamo 的可视化编程环境中使用文本编程?可视化编程有许多优势。它使您无需在直观的可视化界面中学习特殊语法即可创建程序。但是,可视化程序可能会变得混乱,有时可能会在功能上有所降低。例如,Python 提供了更多可实现的方法来编写条件语句 (if/then) 和循环。Python 是一款功能强大的工具,可扩展 Dynamo 的功能,并允许您将许多节点替换为几行简明的代码。
可视化程序:
文本程序:
import clr
clr.AddReference('ProtoGeometry')
from Autodesk.DesignScript.Geometry import *
solid = IN[0]
seed = IN[1]
xCount = IN[2]
yCount = IN[3]
solids = []
yDist = solid.BoundingBox.MaxPoint.Y-solid.BoundingBox.MinPoint.Y
xDist = solid.BoundingBox.MaxPoint.X-solid.BoundingBox.MinPoint.X
for i in xRange:
for j in yRange:
fromCoord = solid.ContextCoordinateSystem
toCoord = fromCoord.Rotate(solid.ContextCoordinateSystem.Origin,Vector.ByCoordinates(0,0,1),(90*(i+j%val)))
vec = Vector.ByCoordinates((xDist*i),(yDist*j),0)
toCoord = toCoord.Translate(vec)
solids.append(solid.Transform(fromCoord,toCoord))
OUT = solids与代码块一样,Python 节点也是可视化编程环境中的脚本编写界面。Python 节点位于库中的“脚本”>“编辑器”>“Python 脚本”下。
双击节点会打开 Python 脚本编辑器(也可以在节点上单击鼠标右键,然后选择 “编辑...” )。您会注意到顶部的一些样本文字,旨在帮助您引用所需的库。输入存储在 IN 数组中。通过将值指定给 OUT 变量,可将这些值返回给 Dynamo
Autodesk.DesignScript.Geometry 库使您能够使用与代码块类似的点符号。有关 Dynamo 语法的详细信息,请参见 https://github.com/DynamoDS/DynamoPrimerNew/blob/master-chs/coding-in-dynamo/7_code-blocks-and-design-script/7-2_design-script-syntax.md以及 DesignScript 手册(要下载此 PDF 文档,请在链接上单击鼠标右键并选择“将链接另存为...”)。键入几何图形类型(如“Point.”),将显示用于创建和查询点的方法列表。
方法包括构造函数(如 ByCoordinates)、操作(如 Add)和查询(如 X、Y 和 Z 坐标)。
单击下面的链接下载示例文件。
可以在附录中找到示例文件的完整列表。
在本示例中,我们将编写一个 Python 脚本,该脚本用于从实体模块创建图案,并将其转换为自定义节点。首先,我们使用 Dynamo 节点创建实体模块。
Rectangle.ByWidthLength:创建一个矩形,它将作为实体的基础。
Surface.ByPatch:将矩形连接到“closedCurve”输入以创建底部曲面。
Geometry.Translate:将矩形连接到“geometry”输入以向上移动它,从而使用代码块指定实体的基础厚度。
Polygon.Points:查询平移的矩形以提取角点。
Geometry.Translate:使用代码块创建与四个点对应的一列四个值,从而向上平移实体的一个角。
Polygon.ByPoints:使用平移的点来重建顶部多边形。
Surface.ByPatch:连接多边形以创建顶部曲面。
现在,我们有了顶面和底面,接下来让我们在两个轮廓之间放样来创建实体的侧面。
List.Create:将底部矩形和顶部多边形连接到索引输入。
Surface.ByLoft:放样两个轮廓以创建实体的侧面。
List.Create:将顶面、侧面和底面连接到索引输入以创建曲面列表。
Solid.ByJoinedSurfaces:连接曲面以创建实体模块。
现在,我们有了实体,接下来将 Python 脚本节点拖动到工作空间。
要向节点添加其他输入,请单击节点上的“+”图标。输入命名为 IN[0]、IN[1] 等,以指示它们表示列表中的各项。
首先定义输入和输出。双击该节点以打开 Python 编辑器。按照下面的代码,在编辑器中修改代码。
# Load the Python Standard and DesignScript Libraries
import sys
import clr
clr.AddReference('ProtoGeometry')
from Autodesk.DesignScript.Geometry import *
# The inputs to this node will be stored as a list in the IN variables.
#The solid module to be arrayed
solid = IN[0]
#A Number that determines which rotation pattern to use
seed = IN[1]
#The number of solids to array in the X and Y axes
xCount = IN[2]
yCount = IN[3]
#Create an empty list for the arrayed solids
solids = []
# Place your code below this line
# Assign your output to the OUT variable.
OUT = solids随着我们在练习中的进展,此代码将更有意义。接下来,我们需要考虑排列实体模块所需的信息。首先,我们需要知道实体的尺寸以确定平动距离。由于存在边界框 Bug,因此我们需要使用边曲线几何图形来创建边界框。
在 Dynamo 中查看 Python 节点。请注意,我们使用的语法与在 Dynamo 中节点标题中看到的语法相同。查看下面注释的代码。
# Load the Python Standard and DesignScript Libraries
import sys
import clr
clr.AddReference('ProtoGeometry')
from Autodesk.DesignScript.Geometry import *
# The inputs to this node will be stored as a list in the IN variables.
#The solid module to be arrayed
solid = IN[0]
#A Number that determines which rotation pattern to use
seed = IN[1]
#The number of solids to array in the X and Y axes
xCount = IN[2]
yCount = IN[3]
#Create an empty list for the arrayed solids
solids = []
#Create an empty list for the edge curves
crvs = []
# Place your code below this line
#Loop through edges an append corresponding curve geometry to the list
for edge in solid.Edges:
crvs.append(edge.CurveGeometry)
#Get the bounding box of the curves
bbox = BoundingBox.ByGeometry(crvs)
#Get the x and y translation distance based on the bounding box
yDist = bbox.MaxPoint.Y-bbox.MinPoint.Y
xDist = bbox.MaxPoint.X-bbox.MinPoint.X
# Assign your output to the OUT variable.
OUT = solids由于我们将平移并旋转实体模块,因此我们使用 Geometry.Transform 操作。通过查看 Geometry.Transform 节点,我们知道需要源坐标系和目标坐标系来变换实体。源是实体的上下文坐标系,而目标是每个阵列模块的不同坐标系。这意味着我们需要遍历 X 和 Y 值,以每次变换不同的坐标系。
# Load the Python Standard and DesignScript Libraries
import sys
import clr
clr.AddReference('ProtoGeometry')
from Autodesk.DesignScript.Geometry import *
# The inputs to this node will be stored as a list in the IN variables.
#The solid module to be arrayed
solid = IN[0]
#A Number that determines which rotation pattern to use
seed = IN[1]
#The number of solids to array in the X and Y axes
xCount = IN[2]
yCount = IN[3]
#Create an empty list for the arrayed solids
solids = []
#Create an empty list for the edge curves
crvs = []
# Place your code below this line
#Loop through edges an append corresponding curve geometry to the list
for edge in solid.Edges:
crvs.append(edge.CurveGeometry)
#Get the bounding box of the curves
bbox = BoundingBox.ByGeometry(crvs)
#Get the x and y translation distance based on the bounding box
yDist = bbox.MaxPoint.Y-bbox.MinPoint.Y
xDist = bbox.MaxPoint.X-bbox.MinPoint.X
#Get the source coordinate system
fromCoord = solid.ContextCoordinateSystem
#Loop through x and y
for i in range(xCount):
for j in range(yCount):
#Rotate and translate the coordinate system
toCoord = fromCoord.Rotate(solid.ContextCoordinateSystem.Origin, Vector.ByCoordinates(0,0,1), (90*(i+j%seed)))
vec = Vector.ByCoordinates((xDist*i),(yDist*j),0)
toCoord = toCoord.Translate(vec)
#Transform the solid from the source coord syste, to the target coord system and append to the list
solids.append(solid.Transform(fromCoord,toCoord))
# Assign your output to the OUT variable.
OUT = solids单击“运行”,然后保存代码。将 Python 节点与现有脚本连接,如下所示。
将 “Solid.ByJoinedSurfaces” 的输出连接为 Python 节点的第一个输入,然后使用“代码块”定义其他输入。
创建 “Topology.Edges” 节点,并使用 Python 节点的输出作为其输入。
最后,创建 “Edge.CurveGeometry” 节点,并使用“Topology.Edges”的输出作为其输入。
尝试更改种子值以创建不同的图案。还可以更改实体模块本身的参数以实现不同的效果。
现在,我们已创建了一个有用的 Python 脚本,接下来我们将它另存为一个自定义节点。选择 Python 脚本节点、在工作空间上单击鼠标右键,然后选择“创建自定义节点”。
指定名称、描述和类别。
这将打开一个新的工作空间,可以在其中编辑自定义节点。
输入:将输入名称更改为更具描述性的名称,并添加数据类型和默认值。
输出:更改输出名称。
将节点另存为 .dyf 文件,然后您应该会看到自定义节点反映了我们刚才所做的更改。

















语言更改部分概述了 Dynamo 中每个版本中对语言所做的更新和修改。这些更改可能会影响功能、性能和使用情况,本指南将帮助用户了解何时以及为何适应这些更新。
将 list@level 语法从“@-1”更改为“@L1”
list@level 的新语法,使用 list@L1 而不是 list@-1
动机:使代码语法与预览/UI 保持一致,用户测试表明这种新语法更易于理解
在 TS 中实现“整数”和“双精度”类型以与 Dynamo 类型保持一致
不允许参数仅因基数而异的重载函数
使用已删除的重载的旧图表应默认为等级较高的重载。
动机:消除关于正在执行哪个特定函数的歧义
使用复制导向禁用阵列升级
使命令式块中的变量成为命令式块范围的局部变量
在命令式代码块中定义的变量值不会因参照它们的命令式块内部的更改而改变。
使变量不可变以禁用代码块节点中的关联更新
将所有 UI 节点编译为静态方法
支持无赋值的 return 语句
“=”在函数定义或命令式代码中都不需要。
CBN 中旧方法名称的迁移
许多节点已重命名,以提高库浏览器用户界面的易读性和位置
作为词典清理列表
已知问题:
命令式块中的命名空间冲突会导致出现意外的输入端口。有关详细信息,请参见 Github 问题。要解决此问题,请在命令式块外部定义函数,如下所示:
pnt = Autodesk.Point.ByCoordinates;
lne = Autodesk.Line.ByStartPointEndPoint;
[Imperative]
{
x = 1;
start = pnt(0,0,0);
end = pnt(x,x,x);
line = lne(start,end);
return = line;
};对 Dynamo 2.0 版本的语言进行了许多改进。这样做的主要动机是简化语言。重点是使 DesignScript 更易于理解和易于使用,从而使其更强大、更灵活,旨在提高最终用户的可理解性。
下面是 2.0 说明中的更改列表:
简化的 List@Level 语法
使用仅按等级不同的参数的重载方法是非法的
将所有 UI 节点编译为静态方法
与复制导向/连缀一起使用时禁用列表提升
关联块中的变量不可变,以防止关联更新
命令式块中的变量是命令式范围的局部变量
列表和词典的分离
list@level的新语法,使用 list@L1 而不是 list@-1
重载函数存在问题的原因有很多:
图表中 UI 节点指示的重载函数可能与运行时执行的重载不同
方法解决成本高昂,不适用于重载函数
很难理解重载函数的复制行为
以 BoundingBox.ByGeometry 为例(旧版本的 Dynamo 中有两个重载函数),一个采用单个值参数,另一个采用几何图形列表作为参数:
BoundingBox BoundingBox.ByGeometry(geometry: Geometry) {...}
BoundingBox BoundingBox.ByGeometry(geometry: Geometry[]) {...}如果用户将第一个节点放在画布上并连接了一组几何图形,则他将期望执行复制,但这永远不会发生,因为在运行时,将改为调用第二个重载,如下所示:
因此,在 2.0 中,我们不允许重载函数,这些函数仅在参数基数上有所不同。这意味着,对于具有相同数量和类型参数但具有一个或多个参数仅等级不同的重载函数,首先定义的重载始终优先,而其余重载则被编译器丢弃。进行这种简化的主要优点是通过选择候选函数的快速路径来简化方法解决逻辑。
在 2.0 的几何图形库中,BoundingBox.ByGeometry 示例中的第一个重载已弃用,第二个重载已保留,因此,如果节点要复制,即在第一个实例的上下文中使用,则需要将其与最短(或最长)连缀选项一起使用,或用于具有复制导向的代码块中:
BoundingBox.ByGeometry(geometry<1>);在此示例中,我们可以看到,等级较高的节点可用于复制和非复制调用,因此始终优于等级较低的重载。因此,根据经验,始终建议节点作者放弃等级较低的重载,转而使用等级较高的方法以便 DesignScript 编译器始终将等级较高的方法称为它找到的第一个也是唯一一个方法。
在下面的示例中,定义了函数 foo 的两个重载。在 1.x 中,哪个重载在运行时执行是模棱两可的。用户可能期望执行第二个重载 foo(a:int, b:int),在这种情况下,该方法需要复制三次,返回值 10 三次。实际上,返回的是单个值 10,因为称为具有 list 参数的第一个重载。
在 2.0 中,始终是定义的第一个方法优先于其余方法被拾取。遵循先到先得原则。
对于以下每种情况,将采用定义的第一个重载。请注意,这纯粹基于函数的定义顺序,而不是参数等级,尽管建议优先使用用户定义节点和 Zero Touch 节点的等级参数较高的方法。
1)
foo(a: int[], b: int); ✓
foo(a: int, b: int); ✕2)
foo(x: int, y: int); ✓
foo(x: int[], y: int[]); ✕在 Dynamo 1.x 中,UI 节点(非代码块)会分别编译为实例方法和特性。例如,Point.X 节点编译为 pt.X,Curve.PointAtParameter 节点编译为 curve.PointAtParameter(param)。此行为有两个问题:
A. UI 节点表示的函数并不总是与运行时执行的函数相同
典型的例子是 Translate 节点。有多个 Translate 节点采用相同数量和类型的参数,例如:Geometry.Translate、Mesh.Translate 和 FamilyInstance.Translate。由于节点是作为实例方法编译的,因此将 FamilyInstance 传递给 Geometry.Translate 节点仍然有效,因为在运行时,它会在 FamilyInstance 上分派对 Translate 实例方法的调用。这显然会误导用户,因为节点没有按照它所说的去做。
B. 第二个问题是实例方法不适用于异构阵列
在运行时,执行引擎需要找出应该分派给哪个函数。如果输入是一个列表,比如 list.Translate(),因为浏览列表中的每个元素并查找其类型的方法的成本很高,因此该方法解决逻辑将简单地假设目标类型与第一个元素的类型相同,并尝试查找在该类型上定义的方法 Translate()。因此,如果第一个元素类型与方法的目标类型不匹配(或者即使它是 null 或空列表),则整个列表都将失败,即使列表中有匹配的其他类型。
例如,如果将具有以下类型 [Arc, Line] 的列表输入传递到 Arc.CenterPoint,则结果将按预期包含圆弧的中心点和直线的 null 值。但是,如果顺序颠倒,则整个结果为 null,因为第一个元素未通过方法解决检查:
x = [arc, line];
y = x.CenterPoint; // y = [centerpoint, null] ✓x = [line, arc];
y = x.CenterPoint; // y = null ✕在 2.0 中,通过将 UI 节点编译为静态属性和静态方法,可以解决这两个问题。
使用静态方法,运行时方法解决更加简单,并且输入列表中的所有元素都会迭代。例如:
foo.Bar()(实例方法)语义需要检查 foo 的类型,还要检查它是否是列表,然后将其与候选函数进行匹配。这很昂贵。另一方面,Foo.Bar(foo)(静态方法)语义只需要检查一个参数类型为 foo 的函数!
以下是 2.0 中发生的情况:
一个 UI 属性节点被编译成一个静态的 getter:插件为每个属性生成一个静态版本的 getter。例如,一个 Point.X 节点被编译成一个静态的 getter Point.get_X(pt)。请注意,静态 getter 也可以在代码块节点中使用其别名 Point.X(pt) 来调用。
UI 方法节点编译为静态版本:引擎为该节点生成相应的静态方法。例如,Curve.PointAtParameter 节点编译为 Curve.PointAtParameter(curve: Curve, parameter:double) 而不是 curve.PointAtParameter(parameter)。
注意: 我们没有通过此更改删除实例方法支持,因此 CBN 中使用的现有实例方法(如上述示例中的 pt.X 和 curve.PointAtParameter(parameter))仍将有效。
此示例以前在 1.x 版本中有效,因为图表编译为 point.X;,并且它将在点对象上找到 X 属性。它现在在 2.0 中失败,因为编译的代码 - Vector.X(point) 只需要一个 Vector 类型:
连贯/可理解: 静态方法可清除有关在运行时执行哪个方法的任何歧义。该方法始终与用户希望调用的图表中使用的 UI 节点匹配。
兼容: 代码和可视化程序之间有更好的相关性。
说明: 现在,将异类列表输入传递给节点会导致节点接受的类型具有非 null 值,而未实现节点的类型会产生 null 值。结果更具可预测性,并且可以更好地指示哪些是节点的允许类型。
由于 Dynamo 通常支持函数重载,因此如果存在具有相同数量参数的另一个重载函数,它可能仍会感到困惑。例如,在下图中,如果我们将数值连接到 Curve.Extrude 的 direction 输入,将向量连接到 Curve.Extrude 的 distance 输入,则两个节点都会继续工作(这是意外情况)。在这种情况下,即使节点编译为静态方法,引擎仍然无法在运行时分辨出差异,而是根据输入类型选择任一方法。
向静态方法语义的转变带来了以下附带影响,这里值得一提的是相关的 2.0 语言变化。
1.多态行为的丧失:
我们来看一个来自 ProtoGeometry 中 TSpline 节点的示例(请注意,TSplineTopology 继承自基础 Topology 类型):以前编译为实例方法 object.Edges 的 Topology.Edges 节点现在编译为静态方法 Topology.Edges(object)。在运行时类型的对象进行方法调度之后,上一个调用将多态解析为派生类方法 TsplineTopology.Edges。
新的静态行为被强制调用基类方法 Topology.Edges。因此,此节点返回基类 Edge 对象,而不是 TSplineEdge 类型的派生类对象。
这是一种回归,因为下游 TSpline 节点期待 TSplineEdges 开始失败。
通过在方法调度逻辑中添加运行时检查来针对方法第一个参数的类型或子类型检查实例类型,从而修复了此问题。在输入列表的情况下,我们简化了方法调度,以简单地检查第一个元素的类型。因此,最终的解决方案是部分静态和部分动态方法查找之间的折衷方案。
2.0 中的新多态行为:
在这种情况下,由于 a 的第一个元素是 TSpline,因此它是在运行时调用的 TSplineTopology.Edges 派生方法。因此,它为基础 Topology 类型 b 返回 null。
在第二种情况下,由于常规 Topology 类型 b 是第一个元素,因此调用了基础 Topology.Edges 方法。由于 Topology.Edges 也恰好接受派生的 TSplineTopology 类型,因此 a 作为输入,它将为输入 a 和 b 返回 Edges。
2.从生成冗余外部列表的回归
在复制导向行为方面,实例方法和静态方法之间有一个主要区别。使用实例方法时,带有复制导向的单值输入不会提升为列表,而对于静态方法,它们会被提升为静态方法。
请考虑具有交叉连缀的 Surface.PointAtParameter 节点示例,该节点具有单个曲面输入以及 u 和 v 参数值的数组。实例方法编译为:
surface<1>.PointAtParameter(u<1>, v<2>);生成点的二维阵列。
静态方法编译为:
Surface.PointAtParameter(surface<1>, u<2>, v<3>);生成具有冗余最外层列表的点的三维列表。
在此类现有用例中,将 UI 节点编译为静态方法的这种附带影响可能会导致回归。此问题已通过在与复制导向/连缀一起使用时禁用将单个值输入提升为列表(请参见下一项)而得到解决。
4.使用复制导向/连缀的禁用列表提升
在 1.x 中,有两种情况将单个值提升为列表:
当较低等级的输入被传递到需要较高等级输入的函数时
当较低等级的输入传递到需要相同等级的函数时,但其中输入参数带有复制导向或使用连缀
在 2.0 中,我们不再通过阻止在这种情况下进行列表升级来支持后一种情况。
在下面的 1.x 图中,y 和 z 中的每一个都有一个级别的复制导向,它们中的每一个都强制阵列提升 1 级,这就是为什么结果的等级为 3(x、y 和 z 各 1 级)。相反,用户希望结果的等级为 1,因为对于单个值输入的复制导向的存在会向结果添加级别并不明显。
x = 1..5;
y = 0;
z = 0;
p = Point.ByCoordinates(x<1>, y<2>, z<3>); // cross-lacing在 2.0 中,每个单值参数 y 和 z 的复制导向的存在不会导致升级,从而导致列表与 x 的输入一维列表具有相同的尺寸。
上述由静态方法编译引起的回归问题,并生成冗余的外部列表,也通过此语言更改得到了解决。
继续上面的相同示例,我们看到像这样的静态方法调用:
Surface.PointAtParameter(surface<1>, u<2>, v<3>); 在 Dynamo 1.x 中生成了三维点列表。发生这种情况的原因是,当与复制导向一起使用时,第一个单值参数曲面被提升为列表。
在 2.0 中,我们已禁用在与复制导向或连缀一起使用时将单值参数提升为列表的功能。所以现在调用:
Surface.PointAtParameter(surface<1>, u<2>, v<3>);只返回一个二维列表,因为曲面没有被提升。
此更改现在删除了冗余列表级别的添加,还解决了过渡到静态方法编译引起的回归。
清晰易读: 结果符合用户预期,更易于理解
兼容: UI 节点(带连缀选项)和使用复制导向的 CBN 可提供兼容的结果
一致:
实例方法和静态方法一致(修复了静态方法语义的问题)
具有输入和默认参数的节点行为一致(请参见下文)
DesignScript 历来支持两种编程范式 - 关联编程和命令式编程。关联代码从变量相互依赖的程序语句创建依存关系图。更新变量可以触发依赖于该变量的所有其他变量的更新。这意味着关联块中语句的执行顺序不是基于它们的顺序,而是基于变量之间的依存关系。
在以下示例中,代码的执行顺序为第 1 行 -> 第 2 行 -> 第 3 行 -> 第 2 行。由于 b 依赖于 a,因此当 a 在第 3 行更新时,执行会再次跳转到第 2 行,以使用 a 的新值更新 b。
1. a = 1;
2. b = a * 2;
3. a = 2;相反,如果在命令式上下文中执行相同的代码,则语句将在自上而下的线性流程中执行。因此,命令式代码块适用于循环和 if-else 条件等代码结构的顺序执行。
1.具有循环依存关系的变量:
在某些情况下,变量之间的循环依存关系可能不像以下情况那样明显。在这种情况下,如果编译器无法静态检测周期,则可能导致无限期的运行时周期。
a = 1;
b = a;
a = b;2.取决于自身的变量:
如果一个变量依赖于它自己,它的值应该累积还是应该在每次更新时重置为它的原始值?
a = 1;
b = 1;
b = b + a + 2; // b = 4
a = 4; // b = 10 or b = 7?在此几何图形示例中,由于立方体 b 取决于自身以及圆柱体 a,移动滑块是否应该使孔沿块移动,还是应该在每次滑块位置更新时创建沿其路径布满多个孔的累积效果?
3.更新变量的属性:
1: def foo(x: A) { x.prop = ...; return x; }
2: a = A.A();
3: p = a.prop;
4: a1 = foo(a); // will p update?4.更新函数:
1: def foo(v: double) { return v * 2; }// define “foo”
2: x = foo(5); // first definition of “foo” called
3: def foo(v: int) { return v * 3; } // overload of “foo” defined, will x update?根据经验,我们发现关联更新在基于节点的数据流图上下文中的代码块节点中被证明是有用的。在任何可视化编程环境可用之前,探索选项的唯一方法是显式更改程序中某些变量的值。基于文本的程序具有变量更新的完整历史记录,而在可视化编程环境中,仅显示变量的最新值。
如果它被一些用户使用过,那么它很可能在不知不觉中被他们使用,弊大于利。因此,我们决定在 2.0 版中通过使变量不可变来隐藏代码块节点使用中的关联性,同时我们继续仅将关联更新保留为 DS 引擎的原生功能。这是为了简化用户的脚本体验而做出的另一项更改。
通过防止变量重定义,在 CBN 中禁用了关联更新:
代码块中仍允许列表索引
列表索引有一个例外,在 2.0 中仍然允许使用索引运算符分配。
在下一个示例中,我们看到列表 a 已初始化,但稍后可以使用索引运算符赋值覆盖,并且依赖于 a 的任何变量都将关联更新,如 c 的值所示。此外,节点预览还将显示在重新定义其一个或多个单元后更新的 a 值。
我们将命令式范围规则更改为 2.0,以禁用复杂的跨语言更新方案。
在 Dynamo 1.x 中,以下脚本的执行顺序为第 1 行 -> 第 2 行 -> 第 4 行 -> 第 6 行 -> 第 4 行,其中更改从外部语言范围传播到内部语言范围。由于 y 在外部关联块中进行了更新,并且命令式块中的 x 依赖于 y,因此控制权从外部关联程序转移到第 4 行中的命令式语言。
1: x = 1;
2: y = 2;
3: [Imperative] {
4: x = 2 * y;
5: }
6: y = 3;下一个示例中的执行顺序为行:第 1 行 -> 第 2 行 -> 第 4 行 -> 第 2 行,其中更改将从内部语言范围传播到外部语言范围。
1: x = 1;
2: y = x * 2;
3: [Imperative] {
4: x = 3;
5: }以上场景指的是跨语言更新,它就像关联更新一样,在代码块节点中不是很有用。为了禁用复杂的跨语言更新方案,我们使命令式范围中的变量变为局部。
在以下示例中,在 Dynamo 2.0 中:
x = 1;
y = x * 2;
i = [Imperative] {
x = 3;
return x;
}命令式块中定义的 x 现在是对命令式范围是局部的
外部范围中的 x 和 y 的值分别保持 1 和 2
如果要在外部范围中访问命令式块中的任何局部变量的值,则需要返回命令式块中的任何局部变量。
请看下面的样例:
1: x = 1;
2: y = 2;
3: [Imperative] {
4: x = 2 * y;
5: }
6: y = 3; // x = 1, y = 3在命令式范围内本地复制 y
对命令式范围局部的 x 的值是 4
由于跨语言更新,在外部范围内更新 y 的值会继续导致 x 更新,但由于变量不可变性,在 2.0 中的代码块中被禁用
外部关联范围内的 x 和 y 的值分别保留 1 和 2
在 Dynamo 1.x 中,列表和词典由单个统一的容器表示,该容器可以通过整数索引和非整型键建立索引。下表总结了 2.0 中列表和词典之间的分离以及新词典数据类型的规则:
列表初始化
a = {1, 2, 3};
a = [1, 2, 3];
空列表
a = {};
a = [];
词典初始化
可以动态附加到同一词典:
只能创建新词典:
a = {};
a = {“foo” : 1, “bar” : 2};
a[“foo”] = 1;
b = {“foo” : 1, “bar” : 2, “baz” : 3};
a[“bar”] = 2;
a = {}; // 创建空词典
a[“baz”] = 3;
词典索引
键索引
索引语法保持不变
b = a[“bar”];
b = a[“bar”];
词典键
任何键类型都是合法的
只有字符串键是合法的
a = {};
a = {“false” : 23, “point” : 12};
a[false] = 23;
a[point] = 12;
[] 列表语法列表初始化语法已从 2.0 中的大括号 {} 更改为方括号 []。在 2.0 中打开时,所有 1.x 脚本都会自动移植为新语法。
有关 Zero Touch 节点上的默认参数属性的注意事项:
但是,请注意,自动迁移将不适用于默认参数属性中使用的旧语法。节点作者需要手动更新其 Zero-Touch 方法定义,以在默认参数属性中使用新语法DefaultArgumentAttribute(如有必要)。
索引注意事项:
在某些情况下,新的索引行为已更改。现在,使用 [] 运算符向包含任意索引/键列表的列表/词典编制索引时,可保留输入的索引/键列表的列表结构。以前,它始终返回一维值列表:
Given:
a = {“foo” : 1, “bar” : 2};
1.x:
b = a[{“foo”, {“bar”}}];
returns {1, 2}
2.0:
b = a[[“foo”, [“bar”]]];
returns [1, [2]];词典初始化的 {}(大括号语法)只能是
dict = {<key> : <value>, …}; 键值对格式,其中 <key> 只允许一个字符串,多个键值对之间用逗号分隔。
Dictionary.ByKeysValues zero-touch 方法可以用作初始化词典的更通用方法,通过分别传入键和值列表并具有使用 zero-touch 方法的所有花里胡哨的功能,如复制导向等。
我们尝试了在词典键值初始化语法中对键使用任意表达式的想法,发现它可能会导致混乱的结果,尤其是当 {keys : vals}(keys,vals都表示列表)等语法干扰 DesignScript 的其他语言功能(如复制)并从 zero touch 初始值设定项节点产生不同的结果时。
例如,可能还有其他情况(如以下语句)很难定义预期行为:
dict = {["foo", "bar"] : "baz" };进一步将复制导向语法等添加到混合,而不仅仅是标识符,将违背语言简单性的想法。
我们 可以 扩展词典键以支持将来的任意表达式,但我们还必须确保与其他语言特性的交互是一致的和可理解的,但代价是增加复杂性,而不是使系统变得不那么强大但易于理解。鉴于总有另一种方法可以解决这个问题,那就是使用 Dictionary.ByKeysValues(keyList, valueList) 方法,这并不是什么大问题。
1.返回 .NET 词典的 Zero Touch 节点会以 Dynamo 词典形式返回
请考虑以下返回 IDictionary 的 zero-touch C# 方法:
相应的 ZT 节点返回值以 Dynamo 词典形式封送处理:
2.多回波节点预览为词典
返回具有多重返回属性的 IDictionary 的 Zero Touch 节点会返回 Dynamo 词典:
3.Dynamo 词典可以作为输入传递到接受 .NET 词典的 Zero-Touch 节点
带有 IDictionary 参数的 ZT 方法:
ZT 节点接受 Dynamo 词典作为输入:
词典是未排序的键值对。与此思路一致,因此不能保证按节点返回值的顺序,对返回词典的节点的键值对预览进行排序。
但是,我们对已定义 MultiReturnAttribute 的多返回节点进行了例外处理。在以下示例中,DateTime.Components 节点是“多返回”节点,节点预览反映其键值对的顺序与节点上的输出端口的顺序相同,这也是根据节点定义上的 MultiReturnAttribute 指定输出的顺序。
另请注意,与 UI 节点不同,代码块的预览不按顺序排列,因为代码块节点的输出端口信息(以多返回属性的形式)不存在:



























