Created on Fri Jan 21 18:00:08 2022; Last updated on 2024-08-31T13:34:53+08:00 @author: Richie Bao

2.1.1 适合写代码的流程

学习任何一门编程语言,首先要找到学习该语言适合的工具(解释器 interpreter 或编译器 compiler)。Python 的解释器很多,但是如果学习的目的是做数据分析,那么Anaconda通常是不二之选;如果要用Flask布局网络平台,则推荐使用PyCharm等。之所以做上述选择是因为这些工具都针对性的减少了写代码过程中的干扰因素和不必要的繁杂事务。这些平台几乎都会解决 Python 库之间的兼容问题,尤其存在几十上百个库时,人为处理库兼容问题是非常困难的;同时,这些工具也为使用者提供了流畅书写代码的工作流,例如 Anaconda 可以非常方便的建立多个环境,在内置安装的 Spyder 等解释器下书写代码,从Console(控制台)下交互打印变量,查看结果调整代码;从Plots下输出图表等。便捷的工作流程可以加快写代码的速度,并尽量避免出现错误的几率。

对于 GH 的 Python Script,是内置于参数化 GH 可视化节点编程下的 Python 编程脚本,因此具有如下特点:一是,Python Script 本身就是解释器,用于书写 Python 代码;但也是 GH 的组件,有输入端口和输出端口,可以实现参数的输入和计算结果的输出,与其它组件相连接,构建参数化程序;二是,Python Script 组件也可以直接作为设计师开发的工具,将具有通用性和频繁使用的代码转换为 GH 的 User Objects(.ghuser)或者.gha(Component)置于 GH 面板下,方便调用,或与他人和团队间分享。

GH 到Version Tuesday,23 July 2024 13.00; Build 1.0.0008版本时,GH 内置的 Python Script 交互式解释器已经发展的很成熟,非常重要的几点有:1,含有帮助文件面板(panel),内置了rhinoscriptsyntaxscriptcontextRhinoCommonGrasshopperEto等 API(application programming interface) 帮助文件,可以不用登录 API 的网页版搜索查看,而本地直接查找;2,具有输入代码时弹出自动提示菜单,辅助查找和补全代码,并有函数、类等功能提示信息,和类似 MSVS(VSC)的智能感知(IntelliSense),方便代码书写和提升工作效率;3,可以在 Python Script 下安装 Python 库,而不再局限于 Python 的内置库,这可以说是解释器最为重要的一项更新,从而不用再为了使用外部库,例如 Numpy 等而不得已用外部解释器处理数据后再调入的棘手问题;4,更方便的调式工具(Debug)。基于 Python Script 解释器的全面升级,推荐首选直接在该解释器中书写代码。如果要使用外部的解释器,例如Visual Studio Code,VSC等,可以将 Python Script 解释器连接到外部的 .py 文件。

Visual Studio Code(VSC) 轻量且功能丰富,可以编辑多种编程语言或文本,可以同时书写 Markdown 等文本文件。也可以选用 Visual Studio(VS)

2.1.1.1 项目文件结构与 GitHub 仓库

1) 在本地建立项目文件夹

parametric_design_coding_GHPython/
├── .dist/
├── .git/
├── docs/
│   └── README.md
├── LICENSE
└── parametric_design_coding_GHPython/
    ├── GH/
    │   ├── 1_versionInfo.gh
    │   └── change_gui.ghx
    ├── ghpy/
    ├── icon/
    │   ├── emoji/
    │   │   ├── goldcrest.png
    │   │   └── versioninfo.png
    │   ├── goldcrest.afdesign
    │   └── png/
    │       ├── coderestart.png
    │       └── versioninfo.png
    ├── resources/
    ├── src/
    │   └── 1_versionInfo.py
    └── userObjects/
        └── about goldcrest.ghuser

可以参考如上的文件夹结构来建立项目文件目录,外层的parametric_design_coding_GHPython为项目根目录(根据具体项目修改名称),内层以同样名字建立的一个文件夹用于存放项目所有代码及相关的数据文件,根据 Python Script 项目特点,包含的子文件夹可以配置有:

  1. GH,用于存放 GH 参数化程序,通常后缀名为 .gh;

  2. ghpy(或命名为 gha),存放编译后的 .gha(用 Python Script 解释器之前版本 ghPython 编译后的后缀名为 .ghpy) 文件,将 .gha文件 置于GH->Params->Special Folders->Components Folder中实现安装,可以显示在 GH 面板中,像一般组件一样调用;

  3. userObjects,为选中 Python Script 组件后,执行File->Create User Object,生成 .ghuser 组件,将其放置于GH->File->Special Folders->User Object Folder中实现安装(安装后,双击该组件可查看编辑),在 GH 面板中出现,像其它组件一样调用;

  4. icon,存储为组件设计的图标。推荐使用Affinity Designer设计图标。图标的模板参考Ladybug Tools/artwork项目,可以直接下载文件Ladybug.afdesign,基于该文件设计新项目的组件图标。图标最终规格为:24×24Pixels大小,300Pixels分辨率的.jpg图像。可以将该图标文件直接拖到 Python Script 的图标上,实现替换(在 1.4 建立模组库部分有详细阐述);

  5. resources,放置相关的数据;

  6. src,Python 代码文件,后缀名为 .py。一种是 Python Script 组件上右键选择Export Script导出的 .py 文件;另一种是外部 Python 解释器书写的代码文件。

根目录下的 docs 文件夹,用于存储该项目的说明文件,通常为 Markdown 书写的文本文件。如果希望将其布局到服务器上,作为小型文档网站分享内容,可以使用docsify布局到 GitHub 上或者其它服务器上。

2)推送至 GitHub 个人仓库

.dist 和 .git 为使用GitHub Desktop,将parametric_design_coding_GHPython项目推送至GitHub个人代码仓库中自动生成的文件。当项目内容发生变化时,可以打开 GitHub Desktop 推送更新。使用 GitHub(版本管理),一是,方便在不同设备上编写代码;二是,避免本地文件丢失或者损坏;三是,专业版本方便团队协作。

2.1.1.2 Python Script

编程语言脚本编辑器(交互式解释器)均在GH->Maths->Script下,包括 C#、VB 和 Python。Python 保留了 Python 2(对应 IronPython 2 Script)版本(图 2.1-1 左下),增加了 Python 3 Script(图 2.1-1 左中),因此推荐使用 Python 3(当前版本为 Python 3.9.10)。也可以使用 Script组件(generic Script component,通用脚本组件)(图 2.1-1 左上),会弹出编程语言选择列表,选择Edit Python 3,就是 Python 3 Script。双击 Python Script 就可以打开脚本编辑器,如图 2.1-1 右。

pys

图 2.1-1 Python Script(脚本编辑器/解释器)

1)组件说明

  • 组件的弹出菜单

可以从 Python Script 组件自身和从打开组件后用 Script Editor(脚本编辑器)书写代码两方面来理解 Python Script。对于组件自身,可以在组件图标(或名称)上右键的弹出菜单开始常用的配置,包括修改组件的名称,例如图 2.1-2中定义了一个类似内置组件Addition的加法运算,将其名称修改为了 Addition,点击文本框右侧图标可以在组件名称和图标之间切换;同时,包括 GH 内置组件常用的功能,如PreviewEnabledBake等;菜单中的Open Script Editor也可以类似双击组件打开编辑器;Export Script则可以导出代码为单独的文件至本地存储,而不嵌入在脚本编辑器组件中。导出代码文件名默认为组件名。如果是[SHIFT+右键],则可以打开扩展功能的弹出菜单,部分功能将在后文阐述。

pys

图 2.1-2 Python Script 组件说明()

代码下载(2_1_01.gh)

  • 组件的输入输出(Input,Output)

在图 2.1-1和图 2.1-2都可以观察到在组件的输入输出端有插入(Insert Parameter,⊕)和移除(Remove Paramert,⊖)输入或输出项的图标。Python Script 组件支持可缩放用户界面(Zoomable User Interface,ZUI),可以通过放大组件来修改组件的输入和输出项。默认情况下,组件的输入项为 xy,输出项为outa。如果任何一侧的参数均移除,则组件该侧以锯齿状边缘表示,表明该组件不需输入参数或没有输出值。在增加输入或输出参数时,会自动配置临时参数名称,这个名称通常需要根据书写代码的内容修改为有意义的名称,具有可读性,易于理解参数输入或输出的目的。与 Python 编程语言一样,在参数命名时需要避开 Python 关键字(Keywords和内置的函数(Built-in Functions名称,如表 2.1-1,避免运行错误或者覆盖(override)已有函数方法,如图 2.1-3。默认的out输出参数可以捕获控制台中print()的打印内容,如图 2.1-2 out输出端输出为当输入参数SwitchFalse时,执行else代码块中的print内容,为"Calculating..."f"A + B = {Result}",后者使用了 Python 的f-string()字面字符串插值(Literal String Interpolation)风格。代码中存在两个print,因此可以看到out输出为分行输出,如果希望将这两句字符串放置于一行,则可以在组件的弹出右键上勾选Avoid Grafting Output Lines,在单行和多行之间切换。如果希望移除out输出项,则可以在组件的弹出右键上取消勾选Standard Output/Error Parameter("out")。取消out输出端,组件将不会尝试捕获输出,这对于较复杂的 GH 程序来说,可以避免每个脚本组件大量额外计算输出,从而提高组件的性能,提升代码的运行效率。

pys

图 2.1-3 关键字和内置函数名作为输入输出参数错误提示

表 2.1-1 关键字和内置函数(Python 3)

关键字(Keywords) 内置函数(Built-in Functions)
False await else import pass None break except in raise True class finally is return and continue for lambda try as def from nonlocal while assert del global not with async elif if or yield A:
abs() aiter() all() anext() any() ascii()
B:
bin() bool() breakpoint() bytearray() bytes()
C:
callable() chr() classmethod() compile() complex()
D:
delattr() dict() dir() divmod()
E:
enumerate() eval() exec()
F:
filter() float() format() frozenset()
G:
getattr() globals()
H:
hasattr() hash() help() hex()
I:
id() input() int() isinstance() issubclass() iter()
L:
len() list() locals()
M:
map() max() memoryview() min()
N:
next()
O:
object() oct() open() ord()
P:
pow() print() property()
R:
range() repr() reversed() round()
S:
set() setattr() slice() sorted() staticmethod() str() sum() super()
T:
tuple() type()
V:
vars()
Z:
zip()
-:
__import__()
  • 类型提示(Type Hints)

变量赋值方式因所用语言是否为静态类型(Static Typing)和动态类型(Dynamic Typing)而有所差异。在静态类型语言中(C、C++、C#等),变量赋值需要先声明(declare)变量,指定数据类型,例如int year = 2024;,这意味着数据类型是已知的,在程序运行之前通常由编译器检查其正确性;而在动态类型中(Python等),变量赋值非常简单,不需要显示地声明变量类型,例如year = 2024,这意味着在程序运行时才知道数据类型。Python Script 组件的输入端默认数据类型为ghdoc Object(即 rhinoscriptsyntax geometry),图 2.1-4 中红色组件输入端A为默认的ghdoc Object,而代码中为加法运算A + B,因此提示为不支持的操作数类型(NoneType)。如果将A的输入类型在其输入端右键弹出菜单中选择Type Hints->float,那么组件颜色为浅灰色,并不会有异常提示。也可以发现在图 2.1-2中,输入端A的输入数据为直线Line,不是float浮点型,但 Python Script 组件仍旧执行了运算,只是此时输入端A的值取自输入直线的长度值。可见 GH 可以对输入值进行自动转换(包括提取几何对象的相关属性值,数值类型转换(例如 intfloat)等)来匹配代码中所需的数据类型。

pys

图 2.1-4 类型提示(Type Hints)示例

  • 参数访问(Parameter Access)

配置输入参数的类型提示相当于对 Python 中的变量像 C/C++/C# 等编程语言一样声明变量类型。另外,在输入端右键弹出菜单中有Item AccessList AccessTree Access三个选项(图 2.1-5),可以配置输入参数的数据结构,类似于 Python 中的单个值、列表(List)和字典(Dict)。在图 2.1-5 配置了三个输入端对应这三种不同的数据结构,内部代码只有一行为AccessType = [type(ItemAccess),type(ListAccess),type(TreeAccess)],传入的数据为0.250的一个浮点数,可以看到输出项标识的数据类型对应到输入端配置的数据结构。如果在配置TreeAccess时,其TypeHints配置为默认值ghdoc Object,输出类型将变为<class 'Grasshopper.DataTree[Object]'>,为Object而不是Double类型。

pys

图 2.1-5 参数访问(Parameter Access)示例

代码下载(2_1_02.gh)

  • 提取参数(Extract Parameter)

从输入端右键弹出菜单中可以选择Extract parameter从组件中提取参数,并保持输入端参数配置的的数据类型(Type Hints)和数据结构(Parameter Access),如图 2.1-6,其输入端xyz的数据类型为float,但数据结构分别对应Item AccessList AccessTree Access,内部脚本代码为a = [x,y,z],对应输出端a的值分别为False<null>empty tree

pys

图 2.1-6 提取参数(Extract Parameter)示例

代码下载(2_1_03.gh)

  • 访问输入、定义输出

图 2.1-2 中定义的 Python Script 组件Addition通过输入端Switch切换两种不同加法定义的方式,如果SwitchTrue,执行类Addition,否则直接执行Result = A + B。输入端参数AB相当于 Python 模块(module)中的全局变量,在该模块中的任何地方(包括函数和类内)均可以访问。在图 2.1-2中定义的类将访问的全局变量AB引用为global,目的是对较为复杂的程序确保不会有与其他同名变量的冲突。将AB求和后的值赋值给同输出端口的变量名Result,会直接从该组件对应端口输出数据。

2)模式(Mode)

Python Script 书写代码的方式有两种,一种称为脚本模式(Script-Mode),另一种称为软件开发工具包模式(SDK-Mode)。图 2.1-2 即为 Script 模式,是相对 SDK 模式更为简单的一种书写模式,是不需要像 SDK 模式定义一个继承 GH 基类Grasshopper.Kernel.GH_ScriptInstance的类。SDK 模式具有典型 GH 组件的特性,包括:

  • BeforeSolveInstance:在组件被要求处理输入端输入参数之前要执行的代码;
  • SolveInstance:输入参数求解,并将结果传递给输出参数;
  • AfterSolveInstance:在组件处理完输入参数求解后执行;
  • DrawViewportWires:在 Rhino 视窗( viewports)绘制图形(相当于绘制 GUI 界面),通常用于信息可视化输出;
  • DrawViewportMeshes:在 Rhino 视窗绘制几何格网(meshes)。

因为可以在 Python Script 中通过继承GH_ScriptInstance而实现上述类似功能,因此称为 SDK 模式。当一开始打开编辑器时,默认为 Script 模式,为常用的一种模式,但不支持在脚本前后运行代码和 Rhino 视窗绘制自定义图形。而在编辑器右上角提供了3个按钮,依次为: 点击Convert script to an implementation of GH ScriptInstance that behaves like a grasshopper component按钮,用默认示例代码转换后的结果为:

  """Grasshopper Script"""
import System
import Rhino
import Grasshopper

import rhinoscriptsyntax as rs

class MyComponent(Grasshopper.Kernel.GH_ScriptInstance):
    def RunScript(self, x, y):
        a = "Hello Python 3 in Grasshopper!"
        print(a)
        
        return
  

可以看到增加了继承GH_ScriptInstance基类的类MyComponent(Grasshopper.Kernel.GH_ScriptInstance),并提供了RunScript()函数,对应SolveInstance功能。点击Click to insert before/after override methods按钮,增加的代码为:

      # Solve overrides 
    def BeforeRunScript(self):
        pass

    def AfterRunScript(self):
        pass
  

增加的代码对应BeforeSolveInstanceAfterSolveInstance功能。点击Click to insert preview override methods按钮,增加的代码为:

      # Preview overrides 
    @property
    def ClippingBox(self):
        return Rhino.Geometry.BoundingBox.Empty

    def DrawViewportWires(self, args):
        pass

    def DrawViewportMeshes(self, args):
        pass
  

增加的代码对应DrawViewportWiresDrawViewportMeshes。为了更明确的解释 SDK 模式,定义SDK-Mode Python Script 组件,如图 2.1-7。图中左侧为 Rhino 视窗,可以看到3个要素,1是,绘制图形(GUI)结果,为包含求和结果语句f"Addition Result={round(self.AdditionResult, 3)}"(即Addition Result=-11.8872文本)和一个灰蓝色底框的 GUI。如果输入端AB值发生了改变,Rhino 视窗中自定义 GUI 的求和信息也会发生变化。该部分功能是由类中的DrawViewportWires()方法实现。2是,在下方有一个灰绿色的方形(实际上为 Mesh 面),是由类中的DrawViewportMeshes()方法实现。这两个方法中均传入了一个参数args,为Grasshopper.Kernel.IGH_PreviewArgs,为预览显示参数(属性),是DisplayPipeline的一个实列,从中可以查找各类绘制方法。3是,右侧包含由3个点连接的一个折线,该部分的实现定义于类中的createPolyline()方法,并在RunScript()方法中调用。

SDK 模式不同于 Script 模式可以全局调用输入端参数,而是需要在类中RunScript()方法中传入后,按类的方法使用变量参数,例如self.mesh = Mesh将输入端参数Mesh转为类的属性self.mesh,从而可以在其它方法中调用。Script 模式的输出端是通过直接赋值至输出端的名称就可以实现数据的输出,而 SDK 模式则需要在RunScript()方法下返回才可以传入到输出端,如 return self.AdditionResult, SubtractionResult, Polyline,也可以将返回值以元组的形式返回,如return (self.AdditionResult, SubtractionResult, Polyline)

为了说明BeforeSolveInstanceAfterSolveInstance功能,在BeforeRunScript()方法中初始化了一个类的属性self.C = 10,及打印字符串Before run script...。从out输出端可以观察到该字符串在执行RunScript()之前得以执行,而且定义的属性self.C可以在addition()方法中使用,不会提示变量未定义的异常。在AfterRunScript()方法中尝试将执行加法后的值转化为其相反数self.AdditionResult *= -1,但是输出结果中并没有变化。同样打印了一行字符串,在输出端out执行完RunScript()之后输出。

Python 的函数签名(Function Signature)是指出参数和返回值的类型。Python Script 脚本编辑器可以根据输入端选择的数据类型(Type Hints)和数据结构(参数访问)自动补全其签名。

pys

图 2.1-7 SDK 模式示例

SDK-Mode (Python Script 组件)

  """Grasshopper Script"""
import System
import Rhino
import Grasshopper
from Grasshopper.Kernel import IGH_PreviewArgs

import rhinoscriptsyntax as rs
import Rhino.Geometry as rg
from System.Drawing import Color

__author__ = "Richie Bao"
__version__ = "2024.09.01"


class MyComponent(Grasshopper.Kernel.GH_ScriptInstance):
    def RunScript(self,
                  A: float,
                  B: float,
                  Pts: System.Collections.Generic.List[object],
                  Mesh: Rhino.Geometry.Mesh,
                  FrontColor: System.Drawing.Color):
        self.mesh = Mesh
        self.frontColor = FrontColor
        print('###', self.frontColor)

        self.GUIParamsConfig()

        self.AdditionResult = self.addition(A, B)
        SubtractionResult = self.subtraction(A, B)
        Polyline = self.createPolyline(Pts)
        print(f"A = {A}; B = {B};C = {self.C}; Pts type: {type(Pts)}")

        return self.AdditionResult, SubtractionResult, Polyline

    # Solve overrides
    def BeforeRunScript(self):
        self.C = 10
        print("Before run script...")

    def AfterRunScript(self):
        self.AdditionResult *= -1

        print(f"After run script : AdditionResult = {self.AdditionResult}")

    # Preview overrides
    @property
    def ClippingBox(self):
        return Rhino.Geometry.BoundingBox.Empty

    def DrawViewportWires(self, args: IGH_PreviewArgs):
        args.Display.Draw2dRectangle(
            System.Drawing.Rectangle(100, 100, 650, 60),
            self.darkGreen,
            2,
            self.lightSlateGray
        )
        pt = rg.Point2d(110, 110)
        args.Display.Draw2dText(f"Addition Result={round(self.AdditionResult, 3)}", self.black, pt, False, 50, "Consolas")


    def DrawViewportMeshes(self, args: IGH_PreviewArgs):
        args.Display.DrawMeshShaded(self.mesh, self.material)

    def addition(self, A, B):

        return A+B+self.C

    def subtraction(self, A, B):

        return A-B

    def createPolyline(self, pts):

        return rs.AddPolyline(pts)

    def GUIParamsConfig(self):
        self.black = Color.Black
        self.lightSlateGray = Color.LightSlateGray
        self.darkGreen = Color.DarkGreen

        self.material = Rhino.Display.DisplayMaterial(Color.Black)
        self.material.Emission = self.frontColor

        # print(help(Color))
  

代码下载(2_1_04.gh)

3)编组(Marshalling)

Python Script 编辑器可以调用多个应用程序接口 (Application Programming Interface,API),其中rhinoscriptsyntax(通常导入时重命名为rs) 引用 Rhino 文档元素是通过其全局唯一标识符(Globally Unique Identifier,GUID)加以引用,其类型为System.Guid;而 RhinoCommon直接引用 Rhino 文档元素,其类型为Rhino.Geometry.BoxRhino.Geometry.Point3d等,因此,Python Script 编辑器通常需要解决这两种类型的相互转化。

  • 编组 GUID(Marshalling Guids)

对于输入的解决方案,如果组件输入端的类型提示(Type Hints)为默认的 ghdoc Object,输入端则自动将其编组为 GUID,以便被rhinoscriptsyntax 中的函数方法调用,例如图 2.1-8中Marshalling-1输入端BoxGHdoc的输入类型为ghdoc Object,在脚本中打印该对象的类型为<class 'System.Guid'>;如果指定了具体的数据类型,例如图 2.1-8示例中输入端的Point配置为Point3dBox配置为Box,则对应的类型为<class 'Rhino.Geometry.Point3d'><class 'Rhino.Geometry.Box'>。对于输出的解决方案,在 Python Script 组件上右键弹出菜单中可以看到有Avoid Marshalling Output Guids选项用于处理输出端的类型转换。组件Marshalling-1并没有勾选Avoid Marshalling Output Guids,可以看到为 GUID 类型的Sphere(sphereID)CPointBoxGHdoc(CBoxGHdoc)的输出端依次对应为Untrimmed Surface{8.811584, 40.955332, 0}Closed Brep。在组件Marshalling-2-2中勾选了Avoid Marshalling Output Guids之后,可以看到输出端ScaleSphere为一个 GUID,类似 1519d35f-8c6a-477d-b130-c6af2a1c7275;而未勾选Avoid Marshalling Output Guids的组件Marshalling-2-1的输出端ScaleSphereUntrimmed Surface

pys

图 2.1-8 编组(Marshalling)示例

在图 2.1-8示例中,组件Marshalling-1的脚本代码为:

  import rhinoscriptsyntax as rs

sphereID = rs.AddSphere(Point, Radius)
Sphere = sphereID
print(f"sphereID:{type(sphereID)}:{sphereID}")

CPoint = rs.AddPoint(Point)
print(f"Point:{type(Point)}; CPoint:{type(CPoint)}")

print(f"BoxGHdoc:{type(BoxGHdoc)}; BoxBox:{type(BoxBox)}")

CBoxGHdoc = BoxGHdoc
print(f"CBoxGHdoc:{type(CBoxGHdoc)}")
  

转化为 SDK 模式后的代码为:

  """Grasshopper Script Instance"""
import System
import Rhino
import Grasshopper

import rhinoscriptsyntax as rs

class MyComponent(Grasshopper.Kernel.GH_ScriptInstance):
   def RunScript(self,
           Radius: float,
           Point: Rhino.Geometry.Point3d,
           BoxGHdoc,
           BoxBox: Rhino.Geometry.Box):
       sphereID = rs.AddSphere(Point, Radius)
       Sphere = sphereID
       print(f"sphereID:{type(sphereID)}:{sphereID}")
       
       CPoint = rs.AddPoint(Point)
       print(f"Point:{type(Point)}; CPoint:{type(CPoint)}")
       
       print(f"BoxGHdoc:{type(BoxGHdoc)}; BoxBox:{type(BoxBox)}")
       
       CBoxGHdoc = BoxGHdoc
       print(f"CBoxGHdoc:{type(CBoxGHdoc)}")

       return (Sphere,CPoint,CBoxGHdoc)
  

转化为 SDK 模式时,编辑器根据输入端选择的类型提示(Type Hints)和参数访问(Parameter Access)自动补全RunScript()方法的签名,即输入参数的类型。组件Marshalling-2-1Marshalling-2-2代码相同,Script 模式代码为:

  import rhinoscriptsyntax as rs

print(f"Sphere:{type(Sphere)}:{Sphere}")

center = rs.SurfaceVolumeCentroid(Sphere)[0]
print(f"center:{type(center)}: {center}")

ScaleSphere = rs.ScaleObject(Sphere, center, [Scale]*3, True)
print(f"ScaleSphere:{type(ScaleSphere)}:{ScaleSphere}")
  

转化为 SDK 模式的代码为:

  """Grasshopper Script Instance"""
import System
import Rhino
import Grasshopper

import rhinoscriptsyntax as rs

class MyComponent(Grasshopper.Kernel.GH_ScriptInstance):
    def RunScript(self, Sphere, Scale: float):
        print(f"Sphere:{type(Sphere)}:{Sphere}")
        
        center = rs.SurfaceVolumeCentroid(Sphere)[0]
        print(f"center:{type(center)}: {center}")
        
        ScaleSphere = rs.ScaleObject(Sphere, center, [Scale]*3, True)
        print(f"ScaleSphere:{type(ScaleSphere)}:{ScaleSphere}")        
        
        return ScaleSphere
  
  • 编组数据类型(Marshalling Data Types (CPython))

Rhino 中 Python 3 的当前实现是使用CPython,与 Rhino SDK(software development kit,软件开发工具包)所基于的.NET 类型不同。对于输入端,在 Script 模式下,.NET 数据类型会自动编组为 Python 3 的数据类型,例如图 2.1-8 组件Marshalling-3-1,输入端Sequence输入的一个列表,自动编组为 Python 3 数据类型Sequence:<class 'list'>;而组件Marshalling-3-2在组件上SHIFT+右键的高级弹出菜单中选择Avoid Marshalling Inputs选项后,打印Sequence的数据类型为Sequence:<class 'System.Collections.Generic.List[Object]'>,是 .NET 的数据类型。当在 SDK 模式下,Avoid Marshalling Inputs会自动被选择,避免编组。对于输出端,高级菜单中可以通过是否勾选Avoid Marshalling Outputs实现是否编组输出端,例如组件Marshalling-3-1勾选后,输出结果CSequence保持了 Python 3 的列表形式,为[1.0, 1.0, 2.0, 3.0, 5.0];而组件Marshalling-3-2,因为输入端保持了 .NET 的数据类型,因此不管是否勾选输出端的Avoid Marshalling Outputs,其输出结果均为 GH 的列表形式。组件Marshalling-3-1Marshalling-3-2的 Script 模式代码为:

  print(f"Sequence:{type(Sequence)}")
CSequence = Sequence
  

对应的 SDK 模式代码为:

  import System
import Rhino
import Grasshopper

import rhinoscriptsyntax as rs

class MyComponent(Grasshopper.Kernel.GH_ScriptInstance):
    def RunScript(self, Sequence: System.Collections.Generic.List[object]):
        print(f"Sequence:{type(Sequence)}")
        
        CSequence = Sequence
        return CSequence
  

2.1.1.1 部分参考:Grasshopper Scripting: Python(New in 8)

代码下载(2_1_05.gh)

4)PyPI 包(Python 库)

Python Script 最大的升级就是支持安装 Python 包(PyPI),虽然部分包的数据结构与当前 GH 的数据类型并不很好兼容,但是在这个数字化和智能化的时代,这无疑为 GH 的发展带来了无限可能。从编辑器工具栏选择Install a third party package for this script可以打开Install Package弹出窗口,指定所要安装的包进行安装,如图 2.1-9。

pys

图 2.1-9 Python 库的安装

相较弹窗方式的安装模式,“程序员”似乎更习惯于在命令行中通过pip install nummpy的方式安装。可以直接从 Windows 的开始按钮右键弹出菜单上选择打开Terminal(Windows PowerShell),定位到 Rhino 的 C:\Users\[username]\.rhinocode\py39-rh8>路径后安装;也可以在RH->Tools->Script->Edit目录下打开Script Editor,然后点击Tools->Advanced->Open Python 3 Shell打开,会直接定位到 Rhino 的 Python 环境目录。如果安装扩展库后,Python Script 找不到该库,可尝试重启 Rhino,以在 Python Script 中调入依赖包。

2.1.1.3 VSC 协作 Python Script

可以使用 Visual Studio Code(VSC)编辑单独保存的.py文件,在空的 Python Script 组件上(最好先移除所有输入端)SHIFT+右键弹出的高级菜单中选择Script Input Parameter ("script"),会生成一个script输入端,此时在该输入端上右键选择Set one script->Set "Python 3" from "Local Drive",选择本地磁盘上的一个 .py 文件后打开,如果有输出数据则配置输出端,会执行该代码;另一种方式,如图 2.1-10中的mothsINFO组件,在一个 Panel 面板中录入(复制) .py 文件路径,连接到File Path组件后连入 Python Script 组件,会提示在输入端右键弹出菜单中选择对应的 Python 3 语言类型,及选择Input Is Path输入方式。可以在File path组件输入端弹出菜单中选择Synchronize,实时同步代码修改结果。接入外部 .py 文件的 Python Script 组件无法双击打开,仅类似一个连接器。

pys

图 2.1-10 外部脚本(.py 文件)调入

mmothsINFO (外部被调用 .py 脚本文件);mothsINFIn (Python Script 组件)

  """
About project Moths.
    Inputs:
        None

    Output:
        info: about project moths        
"""

__author__ = "richiebao (coding-x.tech)"
__version__ = "2024.08.31<-2022.09.23"

ghenv.Component.Name = 'about moths'
ghenv.Component.NickName = 'mothsINFIn'
ghenv.Component.Message = '0.1'
ghenv.Component.Category = 'Moths'
ghenv.Component.SubCategory = 'Info'
ghenv.Component.AdditionalHelpFromDocStrings = '1'

info="author: Richie Bao (coding-x.tech) \nversion:0.1\nproject name: Parametric design programming\ncooperation unit:Guangzhou S.P.I Design Co., LTD"
print("Debugging and Troubleshooting")
print(" ")
print(info)
  

代码下载(2_1_06_versionInfo.gh)

(Python 文件) 2_1_01_versionInfo.py

2.1.2 Python Script 的 API

Python Script 能够调用的库包括两类,一类是 Rhino 提供的 API(Application Programming Interface),可以调用的 Rhino 功能包括, {}Eto, {}GhPython, ghpythonlib, {}Grasshopper, {}Rhino, rhinoscriptsyntax, scriptcontext, {}System等。另一类是python 的标准库,包括:array, binascii, bz2, clr, cmath, copy_reg, cPickle, cStringIO, datetime, errno, exceptions, future_builtins, gc, imp, itertools, marshal, math, mmap, msvcrt, nt, operator, pyexpat, re, select, signal, sys, thread, time, unicodedata, winsound, xxsubtype, zipimport, zlib等。目前 Python Script 可以自行安装 Python 包(PyPI),因此可以调用不计其数的拓展库和自定义的 PyPI 包,例如NumPySciPyMatplotlib,甚至机器学习库scikit-learn等。服务本书开发有mothsPyPI库,可以通过pip install moths方式安装,在 Python Script 中调入使用相关函数方法,也是 Moths 模组中部分 Python Script 组件的依赖库。

2.1.2.1 Rhino 的 API

Rhino Developer Docs - API References中包括 Python Script 编程主要用到 Rhino 的应用程序编程接口 (Application Programming Interface,API),RhinoCommonRhino.Python RhinoScriptSyntaxGrasshopper (Rhino for Windows)

  • RhinoCommon(rc)

RhinoCommon 是 Rhino SDK 可扩展的底层.NET库(API),包括使用 Rhino 所需的类(classes)、结构(structures)和函数(functions),能更多的访问 Rhino 实现的功能(这些功能有些 RhinoScriptSyntax API 无法访问)。使用 RhinoCommon 一般通过访问类和结构创建对象,而不是使用纯粹的函数。但是一旦创建了该对象,就可以返回该对象的属性和方法,也因此提供了更多的功能。用 python 调用 RhinoCommon 时,实际上是执行内嵌到Rhino 平台上的IronPython(一个.NET平台上的 Python 实现,包括完整的编译器,执行引擎,能够与 .NET 已有的库无缝整合到一起)。IronPython通过调用 C#,访问 RhinoCommon。

RhinoCommon 通常为经验丰富的程序员设计,提供更多功能。

  • RhinoScriptSyntax(rs)

RhinoScriptSyntax 复制了 RhinoScript(为 VBScript,目前已经很少人使用)的功能,使用 Python 语言通过 RhinoCommon 库操作运行。RhinoScriptSyntax 方法库包含数百个易于使用的函数,可以执行各种运算,包括几何对象(Geometry),命令(Commands),文档对象(Document objects)和应用方法(Application methods)等。为了使这些方法易于使用,RhinoScriptSyntax 方法都会返回简单的 Python 变量或者基于列表的数据结构。因此一旦熟悉 Python,就很容易使用 RhinoScriptSyntax。同时,RhinoScriptSyntax 的模块划分映射了标准的 Rhino 命令,当查看RhinoScriptSyntax 文档时,会非常方便的找到对应的功能。

RhinoScriptSyntax 通常为普通用户设计,满足大部分基本需求。

  • Grasshopper(Node-in-Code™(nic) form GHPython)

目前几乎每个 GH 组件都可以在 Rhino 的其它地方作为函数被调用,包括 Python Script。这增加了数千个可以访问的函数,包括第三方开发的 GH 组件,并可以把一堆 GH 组件编写的节点可视化代码集成到一个 Python Script 脚本中。这些函数位于ghpythonlib.components下,试图以一个易于调用的形式使用 GH 的每个组件。

Node-in-Code 可以为任何用户使用,这都是一个很有价值的 API。

对于上述 Rhino 的 API 调用,并没有限制的要求,可以由使用者自行确定使用某一种,或者混合使用。在混合调用时,需要注意返回的对象表现形式可能有差异,例如在下述的案例中,用 RhinoCommon 和 Node-in-Code 建立的点返回的为点坐标;但是,用 RhinoScriptSyntax 返回的则是点 GUID,具体可以查看前文编组Marshalling部分。

pys

图 2.1-11 API 示例

RhinoCommonAPI:

  import Rhino.Geometry as rg

Point=rg.Point3d(*CoordiList)
print(Point)
  

grasshopperAPI:

  import ghpythonlib.components as ghc

Point=ghc.ConstructPoint(*CoordiList)
print(Point)
  

RhinoScriptSyntaxAPI:

  import rhinoscriptsyntax as rs

Point=rs.AddPoint(*CoordiList)
print(Point)
  

代码下载(2_1_07.gh)

如果要调用不同的 API,需要查阅对应的文档。Python Script 集成了帮助文件,包括rhinoscriptsyntaxrhinocommonGrasshopper。同时也可以查阅在线文档,包括Rhino Developer Docs 网页文档(RhinoScriptSyntax)RhinoCommon 官网案例Grasshopper SDK等。

2.1.2.2 scriptcontext 模块与 ghDoc 和 rhinoDoc

可以直接在 Rhino 中建立几何对象,及分配几何对象所在的层、材质等属性。GH 是基于 Rhino 的参数化插件,自身同样可以建立和编辑几何对象。Rhino 和 GH 则构成了两个独立而又相互联系的平台。对二者的操作分别处于两个不同的命名空间,rhinoDoc(rhino.DocObjects) 和 ghDoc。rhino.DocObjects不同于上述 API 中的Rhino.Geometry,具有附加到几何对象上的颜色(color)、图层(layer)和基础几何类型构成(geometry)的引用等属性。ghDoc 则由 GH 组件提供以便更好的支持 RhinoScript 库。确切讲,ghDoc 是一个文档的引用,RhinoScript 隐式的通过 Add_()调用。

scriptcontext 模块/库中有一个doc变量,该变量包括当前活动的文档(active document),用户可以将其分配给 ghdoc(默认情况)或者 rhinoDoc 的活动对象(Rhino.RhinoDoc.ActiveDoc)。当分配给Rhino.RhinoDoc.ActiveDoc后,可以从 Python Script 中操作 rhinoDoc,向其访问或添加几何对象的图层等属性。当处理完毕,需要将doc变量重新配置为ghdoc

下述案例,引用了Custom GhPython Baking Component示例,清晰表述了scriptcontextdoc变量运用方法。

pys

图 2.1-12 ghDoc 和 rhinoDoc 示例(bake)

Custom Baking (Python Script 组件)

  """Provides a scripting component.
    Inputs:
        G: Geometry to bake
        L: Layer name for bake
        B: Bake Activate
    Output:
        a: The a output variable"""

__author__ = "ScottD"
__version__ = "2019.08.10"

import rhinoscriptsyntax as rs
import scriptcontext
import Rhino

if B:

    print(type(G)) # debug message to Python output

    # we obtain the reference in the Rhino doc
    doc_object = scriptcontext.doc.Objects.Find(G)
    print(type(doc_object))

    attributes = doc_object.Attributes
    print('the type of attributes is: ' + str(type(attributes)))   # debug message to Python output

    geometry = doc_object.Geometry
    print('the type of geometry is: ' + str(type(doc_object)))     # debug message to Python output

    # we change the scriptcontext
    scriptcontext.doc = Rhino.RhinoDoc.ActiveDoc

    # we add both the geometry and the attributes to the Rhino doc
    rhino_brep = scriptcontext.doc.Objects.Add(geometry, attributes)
    print('the Rhino doc ID is: ' + str(rhino_brep))     # debug message to Python output

    # we can for example change the layer in Rhino...
    if not rs.IsLayer(L):
        rs.AddLayer(L)
    rs.ObjectLayer(rhino_brep, L)

    # we put back the original Grasshopper document as default
    scriptcontext.doc = ghdoc
    a = G
  

代码下载(2_1_08.gh)

2.1.3 编译 Python Script 为 .gha 文件

在 1.4 部分阐述了建立 User Object 对象(.ghuser)的方法,将其置于GH->File->Special Folders->User Object Folder指定的路径下(通常位于C:\Users\[username]\AppData\Roaming\Grasshopper\UserObjects),从而加载到 GH 的工具面板中方便调用。.ghuser 组件并不需要作者执行编译(compile),但用户可以打开封装的组件或 Python Script 查看源码(可以在封装组件上右键弹出菜单中通过Assign Password配置密码)。如果作者希望执行编译,在之前的 GHPython 脚本编辑器版本下,可以通过GHPython->Mode->GH_Component SDK Mode转换为 SDK 模式后,再执行Mode->Compile编译为.ghpy文件,将被置于GH->File->Special Folders->Components Folder中实现安装(其放置路径通常为C:\Users\[username]\AppData\Roaming\Grasshopper\Libraries),在 GH 面板中出现,像其它组件一样调用,并不可查看源码。目前 Rhino(Version 8 SR10)更新了这一方法,取消了 GH 中 Python Script 脚本编辑器中的编译按钮,

  1. 而先在RH->Tools->Script->Edit中打开脚本编辑器Script Editor,如图 2.1-13_1;
  2. 然后再在编辑器下点击File->Create Project建立新项目,如图 2.1-13_2,填写相关信息,包括项目名称Name、项目描述Description、版本Version、作者Author、版权Copyright、许可证License和网址URL等。建立项目后会出现类似图 2.1-13_1中的项目结构,在Components上右键(Add Grasshopper 1 Script)可以添加文件,打开包含有 Python Script 组件事先保存的 .gh 文件,则该文件会被添加到Components下,并自动识别 Python Script 组件,如图 2.1-13_1中的Panel Style,双击该组件则可以打开对应的 .gh 文件和源码;
  3. 选中添加的Panel Style组件后,在该子面板上方工具栏选择Edit selected project item编辑按钮打开组件编辑面板Edit Components "Panel Style",如图 2.1-13_3,可以配置该组件的图标、名称Name、别名Nickname、描述Description、子分类Sub-Category和位置Exposure等;
  4. 在 GH 下将制作的 PNG 格式图标拖到组件上所显示的图标并不能在编译时作为图标使用,需要使用 SVG 格式文件,可以通过Affinity Designer导出为 SVG 格式。点击图 2.1-13_3右上角的图标会打开图标编辑器Edit Image,点击其File->Import调入图标文件,如图 2.1-13_4 ;
  5. 最后是发布项目,点击Script EditorFile->Publish Project将弹出图 2.1-13_5窗口,会自动填充之前的配置,默认编译路径为文件所在文件夹下的build目录下(可以根据情况修改),点击Build Package,编译项目;
  6. 打开build文件夹,可以看到在rh8文件夹下包含一个Moths.gha文件,该文件可以包含多个编译的 Python Script 组件(示例中只有一个),将其置于GH->File->Special Folders->Components Folder中实现安装,就可以在 GH 面板Moths模组下的GUI中看到加载的Panel Style组件,该组件可以修改 GH 画布中 Panel面板中字体的类型和大小,及面板的颜色,通常适合用于编辑样式后用于图片形式的代码发布分享,例如用于书籍插图等,如图 2.1-13_6。
pys

图 2.1-13_1 项目/编辑器窗口(Script Editor

pys

图 2.1-13_2 建立项目(Create Project

pys

图 2.1-13_3 编辑选择的项 (Edit selected project item

pys

图 2.1-13_4 编辑图标 (Edit Icon)

pys

图 2.1-13_5 发布项目(Publish Project

pys

图 2.1-13_6 调用发布编译后的 Font Style 组件

Font Style (Python Script 组件)

  """Grasshopper Script Instance"""

import System
import Rhino
import Grasshopper

import rhinoscriptsyntax as rs
from System.Drawing import Color

class MyComponent(Grasshopper.Kernel.GH_ScriptInstance):
    def RunScript(self, Font, Size, Color, DefaultPanel: bool):
        if Font and Size:
            f = System.Drawing.Font(Font,int(Size))
        elif Font:
            f = System.Drawing.Font(Font,self.defaultFont.Size)
        elif Size:
            f = System.Drawing.Font(self.defaultFont.Name,int(Size))   
        else:
            f =  self.defaultFont

        if DefaultPanel:
            f = self.defaultFont            

        for obj in ghenv.Component.OnPingDocument().Objects:
            if isinstance(obj,Grasshopper.Kernel.Special.GH_Panel):
                obj.Properties.Font = f
                obj.ExpireSolution(True)
                if Color and DefaultPanel is not True:
                    obj.Properties.Colour = Color
                elif DefaultPanel:
                    obj.Properties.Colour = self.defaultColor
                else:
                    obj.Properties.Colour = self.defaultColor
        
        return

    # Solve overrides 
    def BeforeRunScript(self):
        fontDict=dict(Name="Courier New", Size=3.5555556, Units=3, GdiCharSet=1, GdiVerticalFont=False)
        # Color [A=255, R=255, G=250, B=90]
        self.defaultFont=System.Drawing.Font(fontDict["Name"],fontDict["Size"])    
        self.defaultColor= Color.FromArgb(255, 250, 90)   

    def AfterRunScript(self):
        pass
  

代码下载(2_1_09.gh)

关于rhino下python编程,更多资源可以查看官网Rhino.Python Guides

注释(Notes):

① Anaconda,用于科学计算(数据科学、机器学习应用、大数据处理、预测分析等)的 Python 和 R 编程语言的环境管理器,旨在简化包管理和部署(https://www.anaconda.com/)。

② Flask,是一个用 Python 编写的 Web 应用程序框架,基于Werkzeug WSGI工具包和Jinja2模板引擎(https://flask.palletsprojects.com/en)。

③ PyCharm,是一个流行的,用于 Python 开发的集成开发环境(IDE),由 JetBrains 创建,提供了一系列使 Python 编程更高效、更有趣的特性(https://www.jetbrains.com/pycharm/)。

④ Visual Studio Code(VSC),由 Microsoft 开发的一个非常流行、轻量级、且功能强大的代码编辑器,被广泛用于 Python 开发,并提供了各种使编码更高效的特性(https://code.visualstudio.com/)。

⑤ Markdown,是一种轻量级标记语言(markup language),为使用纯文本编辑器创建格式化文本。于2004 年,由 John Gruber 创建,广泛用于博客(bloggnig)、即使消息(instant messaging)传递、在线论坛(online forums)、写作软件(collaborative software)、文档(documentation)页面和自述文件(readme files)等用途(https://en.wikipedia.org/wiki/Markdown)。

⑥ Visual Studio(VS),为用 .NET 和 C++、C# 等,在 Windows 上构建网页(web)、云(cloud)、桌面(desktop)、移动(mobile)应用程序,服务和游戏最为综合的 IDE(https://visualstudio.microsoft.com/)。

⑦ Affinity Designer,该软件服务于插画家、设计师、游戏开发者等设计类专业人士,用于创建数字插图、概念艺术、独特的图形、徽标、品牌设计和网络模型(web mock-ups )等(https://affinity.serif.com/en-us/)。

⑧ Ladybug Tools/artwork,Ladybug 源码开发的 GitHub 代码托管仓库(https://github.com/ladybug-tools/artwork)。

⑨ docsify,通过智能的加载和解析Markdown文件,即时显示生成一个网站。可以通过配置index.html文件,部署到GitHub页面上,发布网站(https://docsify.js.org/#/)。

⑩ GitHub Desktop,用于管理托管于 GitHub 上 Git 存储库的一个用户友好的图形界面,其简化了 Git 的工作流程,使用户无需使用命令行就可以轻松管理自己的项目(https://github.com/apps/desktop)。

⑪ GitHub,是一个基于 Web,用于版本控制和协作软件开发的平台,使用开源版本控制系统 Git 来帮助开发人员管理和跟踪代码库的变化,支持协作、项目管理和代码审查等功能(https://github.com/)。

⑫ Keywords,Python 编程语言关键字(https://docs.python.org/3/reference/lexical_analysis.html#keywords)。

⑬ Built-in Functions,Python 编程语言内置函数(https://docs.python.org/3/library/functions.html#built-in-functions)。

⑭ BeforeSolveInstance,(https://developer.rhino3d.com/api/grasshopper/html/M_Grasshopper_Kernel_GH_Component_BeforeSolveInstance.htm)。

⑮ SolveInstance,(https://developer.rhino3d.com/api/grasshopper/html/M_Grasshopper_Kernel_GH_Component_SolveInstance.htm)。

⑯ AfterSolveInstance,(https://developer.rhino3d.com/api/grasshopper/html/M_Grasshopper_Kernel_GH_Component_AfterSolveInstance.htm)。

⑰ DrawViewportWires,(https://developer.rhino3d.com/api/grasshopper/html/M_Grasshopper_Kernel_GH_Component_DrawViewportWires.htm)。

⑱ DrawViewportMeshes,(https://developer.rhino3d.com/api/grasshopper/html/M_Grasshopper_Kernel_GH_Component_DrawViewportMeshes.htm)。

⑲ Grasshopper.Kernel.IGH_PreviewArgs,(https://developer.rhino3d.com/api/grasshopper/html/T_Grasshopper_Kernel_GH_PreviewArgs.htm)。

⑳ DisplayPipeline,(https://developer.rhino3d.com/api/rhinocommon/rhino.display.displaypipeline)。

㉑ CPython,是 Python 编程语言原始的参考实现,为大多数 Python 程序员使用的标准版本(默认),使用 C 语言编写,因此称为 CPython。当编写 Python 代码时,首先将 Python 代码编译为字节码(bytecode),通常以 ·.pyc· 文件存储于 ·pycache·下。随后,由 Python 虚拟机(Python Virtual Machine,PVM)执行,对字节码指令进行解释,并执行相应操作(https://devguide.python.org/internals/exploring/)。

㉒ PyPI,为 Pyton 包索引,是 Python 包的官方存储库。开发者可以在 PyPI 平台上传和分享 Python 项目,从而可以通过pip 方式安装 (https://pypi.org/)。

㉓ python 的标准库,是 Python 附带的模块和包的集合,提供了广泛的即用功能,是 Python 的组成部分,包括了常用功能,开发人员无需安装额外的包就可使用(https://docs.python.org/2/library/)。

㉔ NumPy,是 Python 中用于数值计算的基本库,支持大型多维数组(arrays)和矩阵(matrices),及对这些数组进行操作的各类数学函数,广泛应用于数据分析、科学计算和机器学习等领域(https://numpy.org/)。

㉕ SciPy,是以 NumPy 的功能为基础用于科学计算的 Python 库,为各种科学领域的任务提供广泛的功能支持,尤其适用于高级数学计算和数据分析任务(https://scipy.org/)。

㉖ Matplotlib,是一个用于在 Python 中创建静态、动画和交互式可视化的综合库(https://matplotlib.org/)。

㉗ scikit-learn,是功能强大且广泛使用的 Python 机器学习库,为数据挖掘和数据分析提供了简单有效的工具(https://scikit-learn.org/stable/)。

㉘ moths,为本书 Moths 模组开发的 Python 库(https://pypi.org/project/moths/)。

㉙ Rhino Developer Docs - API References,Rhino 和 Grasshopper 的 API 参考(https://developer.rhino3d.com/api/)。

㉚ IronPython,是紧密集成 .NET 的 Python 编程语言开源实现(implementation),可以使用 .NET 和 Python 库;而 .NET 语言也可以很容易的使用 Python 代码(https://ironpython.net/)。

㉛ RhinoScriptSyntax 文档,(https://developer.rhino3d.com/api/RhinoScriptSyntax/#application)。

㉜ RhinoCommon 官网案例,(https://developer.rhino3d.com/samples/#rhinocommon)。

㉝ Grasshopper SDK,(https://developer.rhino3d.com/api/grasshopper/html/723c01da-9986-4db2-8f53-6f3a7494df75.htm)。

㉞ Custom GhPython Baking Component,(https://developer.rhino3d.com/guides/rhinopython/ghpython-bake/)。

㉟ Rhino.Python Guides,(https://developer.rhino3d.com/guides/rhinopython/)。