关于 rhino3d-scripts
rhino3d-scripts 是面向 Rhinoceros 3D 脚本编写、修改和调试的技能,适用于自动化建模及维护 Rhino 脚本的开发者。它涵盖 RhinoScript、RhinoPython、RhinoCommon 和命令宏,并提供工作流与故障排查指导。
主要功能
- 01涵盖三种 Rhino 脚本接口
- 02指导选择脚本或宏
- 03提供几何处理代码模式
- 04说明脚本加载与分发
- 05整理常见问题及修复方法
适用场景
- 01编写或调试 RhinoPython 脚本
- 02维护 VBScript 或 RhinoScript 文件
- 03批量修改 Rhino 对象
- 04配置脚本启动或工具栏按钮
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ github/ awesome-copilot --skill rhino3d-scripts安装后在 Claude Code 中输入/rhino3d-scripts确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
references/macros-and-loading.mdrhinocommon-map.mdrhinoscriptsyntax-cheatsheet.mdvbscript-quirks.md详细介绍
Rhino 3D 脚本技能
为 Rhinoceros 3D 编写生产级质量的脚本。涵盖三种脚本编写方式(RhinoScript/VBScript、RhinoPython、直接使用 RhinoCommon .NET)以及 Rhino 8+ 脚本编辑器。
何时使用本技能
- 用户要求编写、编辑或调试
.rvb、.vbs或.pyRhino 脚本 - 用户想要一个 Rhino 命令宏,或想要自动化执行一系列 Rhino 命令
- 用户想要通过代码操作几何体、图层、图块、材质、视口或注释
- 用户提到
rhinoscriptsyntax、scriptcontext、RhinoCommon、Rhino.Geometry、RhinoDoc或脚本编辑器 - 用户想要在 Rhino 中拾取对象、提示输入,或构建一个小型 UI
- 用户询问如何加载、运行或分发脚本(启动脚本、别名、工具栏按钮)
选择脚本编写方式
根据任务而非偏好来选择方式。对新脚本,默认推荐 Python。
| 方式 | 何时选择 | 文件扩展名 |
|---|---|---|
RhinoPython(rhinoscriptsyntax + RhinoCommon) | 新脚本的默认选择。生态最佳、可读性好、可完全访问 RhinoCommon。 | .py |
| RhinoScript(VBScript) | 维护遗留的 .rvb/.vbs 文件;与 VBA/COM 集成。 | .rvb、.vbs |
| 通过脚本编辑器使用 RhinoCommon(C#/.NET) | 性能关键的循环、复杂几何体、利用 .NET 库。 | .cs |
| 命令宏 | 纯粹是现有 Rhino 命令的序列;没有逻辑。 | 工具栏/别名 |
宏不是脚本——它是一串命令行输入(例如 ! _-Line 0,0,0 10,0,0 _Enter)。一旦需要变量、循环或条件判断,就应使用脚本。
前置条件
- Rhino 7 或更高版本(强烈推荐 Rhino 8——统一的脚本编辑器在同一个窗口中支持 Python 3、VB 和 C#)。
- 脚本编辑器:输入
_ScriptEditor(Rhino 8)或_EditPythonScript/_EditScript(较旧版本)。 - 在命令行中使用
_-RunPythonScript或_LoadScript+_RunScript运行已保存的文件。
核心模式
Python:最小脚手架
import rhinoscriptsyntax as rs
import scriptcontext as sc
import Rhino
def main():
obj_id = rs.GetObject("Select a curve", filter=rs.filter.curve, preselect=True)
if not obj_id:
return
length = rs.CurveLength(obj_id)
print("Length: {0:.4f}".format(length))
if __name__ == "__main__":
main()
Python:直接使用 RhinoCommon
import Rhino
import scriptcontext as sc
doc = sc.doc # Rhino.RhinoDoc.ActiveDoc
tol = doc.ModelAbsoluteTolerance
circle = Rhino.Geometry.Circle(Rhino.Geometry.Point3d(0, 0, 0), 5.0)
curve_id = doc.Objects.AddCircle(circle)
doc.Views.Redraw()
VBScript:最小脚手架
Option Explicit
Call Main()
Sub Main()
Dim strObject
strObject = Rhino.GetObject("Select a curve", 4) ' 4 = curve filter
If IsNull(strObject) Then Exit Sub
Rhino.Print "Length: " & Rhino.CurveLength(strObject)
End Sub
使用自定义过滤器拾取对象(Python、RhinoCommon)
import Rhino
import scriptcontext as sc
go = Rhino.Input.Custom.GetObject()
go.SetCommandPrompt("Select breps")
go.GeometryFilter = Rhino.DocObjects.ObjectType.Brep
go.SubObjectSelect = False
go.GetMultiple(1, 0)
if go.CommandResult() != Rhino.Commands.Result.Success:
pass
else:
ids = [go.Object(i).ObjectId for i in range(go.ObjectCount)]
分步工作流
快速批量修改大量对象
- 禁用重绘:
rs.EnableRedraw(False)。 - 将修改操作包装在单个撤销记录中:
undo = doc.BeginUndoRecord("My Op")…doc.EndUndoRecord(undo)。 - 在循环内直接使用 RhinoCommon(跳过
rhinoscriptsyntax的开销)。 - 重新启用重绘,并在
try/finally中调用doc.Views.Redraw(),这样即使崩溃也绝不会让视口卡死。
将脚本分发给队友
- 将
.py/.rvb保存到磁盘上的某个位置。 - 将该文件夹添加到
Options → Files → Search paths,以便 Rhino 能按名称找到它。 - 创建一个工具栏按钮或别名,其宏为:
- Python:
! _-RunPythonScript "MyScript.py" - RhinoScript:
! _-LoadScript "MyScript.rvb" _-RunScript MySubName
- Python:
- 开头的
!会取消任何正在运行的命令;-以脚本(无对话框)模式运行命令。
在 Rhino 启动时运行代码
- 将
.rvb/.py放在某个搜索路径中。 Tools → Options → RhinoScript(或Python)→ 添加到 Startup 列表。该文件会在每个会话中执行一次。
注意事项
rhinoscriptsyntax返回 GUID,RhinoCommon 返回对象。 混用两者没问题,但doc.Objects.Find(guid)是从rs.*id 到RhinoObject的桥梁。- 坐标因曲面而异。 Python 使用
(x, y, z)元组或Rhino.Geometry.Point3d;VBScript 使用 3 元素Array(x, y, z)。切勿通过 COM 将 Python 列表传递给 VBScript 辅助函数。 - VBScript 中
Option Explicit默认关闭。 拼写错误会静默创建新变量。务必在.rvb文件顶部添加Option Explicit。 - VBScript 没有块级作用域。
Sub内的所有Dim都会被提升到过程顶部。循环计数器会泄漏。 - 在 VBScript 中
Nothing、Empty和Null各不相同。 对Rhino.GetObject失败使用IsNull,对未初始化的Variant使用IsEmpty,对对象引用使用Is Nothing。 - 括号会改变 VBScript 中的调用语义。
Call Foo(a, b)和Foo a, b有效;Foo(a, b)(没有Call、带括号)不是对 Sub 的调用——对于多参数 sub 是语法错误,对于单参数则是强制ByVal。 - 容差是按文档设置的。 始终读取
doc.ModelAbsoluteTolerance,而不是硬编码0.001;用户可能使用 mm、m、英寸等。 - 长时间循环应轮询
Rhino.RhinoApp.EscapeKeyPressed,以便用户可以取消。否则 Rhino 会显得卡死。 - GUID 字符串与
System.Guid。rhinoscriptsyntax两者都接受;RhinoCommon 需要System.Guid。如有需要,使用System.Guid(str_id)转换。 - 不要在紧凑循环内调用
doc.Views.Redraw()。 在循环外一次性切换重绘。 .rvb只是重命名的.vbs,带有一个 Rhino 专用扩展名,以便 Rhino 的LoadScript识别它。相同的 VBScript 引擎。Rhino.RhinoApp.IsHeadless在较旧的 Rhino 8 版本上可能不存在。 使用getattr(Rhino.RhinoApp, "IsHeadless", None),并在使用前检查是否为None。回退到启发式判断(例如sc.doc.Views.Count == 0)或假定存在 GUI。RhinoMath位于Rhino.RhinoMath,而非Rhino.DocObjects.RhinoMath。 访问Rhino.DocObjects.RhinoMath会引发AttributeError。doc.Objects.AddBrep()失败时返回System.Guid.Empty。 在 Rhino 8 CPython 中,System命名空间可能无法直接导入;以字符串形式检查返回值:str(obj_id) == "00000000-0000-0000-0000-000000000000"。rhinoscriptsyntax没有类型存根。 静态分析器(Pylance/Pyright)会将import rhinoscriptsyntax as rs标记为无法解析。在导入行上使用# type: ignore抑制警告;该模块在 Rhino 运行时始终可用。- 切勿以 Python 标准库模块命名脚本(例如
random.py、math.py、os.py)。IronPython 2.7(_-RunPythonScript)在标准库之前解析脚本目录,因此标准库内的任何import random(例如tempfile内部导入random)都会找到你的文件并失败,报错Cannot import name <X>。CPython 3(rhinocode)不受影响,因为它先解析标准库。重命名脚本或避免导入会拉入被遮蔽名称的模块。 - 破折号和其他非 ASCII 字符会静默破坏
_-RunPythonScript(IronPython 2.7)。rhinocode script使用 CPython 3(默认 UTF-8),因此同一文件在那里可以正常工作,使得故障不明显。IronPython 2.7 在第一个违规字节处引发SyntaxError: Non-ASCII character '\xe2'。最常见的罪魁祸首是破折号(许多编辑器会自动将--转换为--)。在每个必须在两种运行时下运行的脚本的第 1 行添加# -*- coding: utf-8 -*-,并将排版字符替换为 ASCII 等价物:破折号--、箭头->、乘号x。
故障排查
| 症状 | 解决方法 |
|---|---|
rs.GetObject 立即返回 None | 用户按下了 Escape,或者你的 filter 排除了所有内容。重新检查 rs.filter.* 标志。 |
| 按名称运行脚本时提示“Unable to find script” | 该文件夹不在 Options → Files → Search paths 中。 |
VBScript 在坐标上出现 Type mismatch | 你传入了一个 2 元素数组。Rhino 要求使用 3 元素的 Array(x, y, z)。 |
Python 出现 ImportError: No module named Rhino | 你在 Rhino 之外运行 CPython。RhinoCommon 仅在 Rhino 内嵌的 Python 中可用(或者通过 rhino3dm 进行只读文件操作)。 |
| 创建的几何体没有出现 | 你忘记了 doc.Views.Redraw(),或者 rs.EnableRedraw(False) 从未被重新启用。 |
| 撤销只撤销了批量操作中的最后一个对象 | 将该批量操作包裹在 BeginUndoRecord / EndUndoRecord 中。 |
| 脚本单独运行正常,但作为启动脚本时失败 | 启动脚本在任何文档打开之前运行 — 在 sc.doc is None 时提前返回或跳过依赖文档的工作。 |
rs.Command("...") 返回 False | 宏字符串格式有误。以 ! 和 - 为前缀,每个提示以 _Enter 或一个值结尾。 |
AttributeError: type object 'RhinoApp' has no attribute 'IsHeadless' | 该属性是在后续的 Rhino 8 版本中添加的。使用 getattr(Rhino.RhinoApp, "IsHeadless", None) 并对 None 进行防护。 |
rhinocode script 忽略脚本路径之后的参数 | rhinocode 会将额外的 token 拼接到文件 URI 上。请通过临时文件或 Rhino 对话框传递数据。参见 references/macros-and-loading.md。 |
使用 _-RunPythonScript 时,标准库内部(例如 tempfile、os)出现 Cannot import name <X> | 脚本文件名遮蔽了某个标准库模块(例如 random.py 遮蔽了 random)。IronPython 2.7 会先搜索脚本目录,再搜索标准库。请重命名脚本,或移除引入被遮蔽模块的 import 语句,并替换为直接的替代方案(例如通过 os.environ 读取 %TEMP%,而不是 import tempfile)。 |
SyntaxError: Non-ASCII character '\xe2' ... but no encoding declared | IronPython 2.7(_-RunPythonScript)遇到了 em dash 或类似字符。请在第 1 行添加 # -*- coding: utf-8 -*-,或替换该字符:em dash 改为 --,箭头改为 ->。同一文件在 rhinocode(CPython 3)下运行正常,这会掩盖该问题。 |
参考
- references/rhinoscriptsyntax-cheatsheet.md — 按类别划分的最常用
rs.*函数。 - references/rhinocommon-map.md — 哪种任务应导入哪个命名空间。
- references/macros-and-loading.md — 命令行宏语法、
LoadScript/RunScript、搜索路径。 - references/vbscript-quirks.md — 与 RhinoScript 相关的 VBScript 特有陷阱。
上游文档
- RhinoScript 主页:https://docs.mcneel.com/rhino/8/help/en-us/information/rhinoscripting.htm
- 开发者中心:https://developer.rhino3d.com/
- RhinoCommon API 索引:https://mcneel.github.io/rhinocommon-api-docs/api/RhinoCommon/html/R_Project_RhinoCommon.htm
- 示例脚本仓库:https://github.com/mcneel/rhino-developer-samples/tree/8/rhinoscript
评论
支持 Markdown