mcpField
R
rhino3d-scripts

rhino3d-scripts

github.com/github创意与媒体40.5k92 次安装1 天前更新
代码仓库

关于 rhino3d-scripts

rhino3d-scripts 是面向 Rhinoceros 3D 脚本编写、修改和调试的技能,适用于自动化建模及维护 Rhino 脚本的开发者。它涵盖 RhinoScript、RhinoPython、RhinoCommon 和命令宏,并提供工作流与故障排查指导。

主要功能

  1. 01涵盖三种 Rhino 脚本接口
  2. 02指导选择脚本或宏
  3. 03提供几何处理代码模式
  4. 04说明脚本加载与分发
  5. 05整理常见问题及修复方法

适用场景

  1. 01编写或调试 RhinoPython 脚本
  2. 02维护 VBScript 或 RhinoScript 文件
  3. 03批量修改 Rhino 对象
  4. 04配置脚本启动或工具栏按钮

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/github/awesome-copilot --skill rhino3d-scripts

安装后在 Claude Code 中输入/rhino3d-scripts确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

references/
macros-and-loading.md
rhinocommon-map.md
rhinoscriptsyntax-cheatsheet.md
vbscript-quirks.md

详细介绍

Rhino 3D 脚本技能

为 Rhinoceros 3D 编写生产级质量的脚本。涵盖三种脚本编写方式(RhinoScript/VBScript、RhinoPython、直接使用 RhinoCommon .NET)以及 Rhino 8+ 脚本编辑器。

何时使用本技能

  • 用户要求编写、编辑或调试 .rvb、.vbs 或 .py Rhino 脚本
  • 用户想要一个 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)]

分步工作流

快速批量修改大量对象
  1. 禁用重绘:rs.EnableRedraw(False)。
  2. 将修改操作包装在单个撤销记录中:undo = doc.BeginUndoRecord("My Op") … doc.EndUndoRecord(undo)。
  3. 在循环内直接使用 RhinoCommon(跳过 rhinoscriptsyntax 的开销)。
  4. 重新启用重绘,并在 try/finally 中调用 doc.Views.Redraw(),这样即使崩溃也绝不会让视口卡死。
将脚本分发给队友
  1. 将 .py / .rvb 保存到磁盘上的某个位置。
  2. 将该文件夹添加到 Options → Files → Search paths,以便 Rhino 能按名称找到它。
  3. 创建一个工具栏按钮或别名,其宏为:
    • Python:! _-RunPythonScript "MyScript.py"
    • RhinoScript:! _-LoadScript "MyScript.rvb" _-RunScript MySubName
  4. 开头的 ! 会取消任何正在运行的命令;- 以脚本(无对话框)模式运行命令。
在 Rhino 启动时运行代码
  1. 将 .rvb/.py 放在某个搜索路径中。
  2. 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 declaredIronPython 2.7(_-RunPythonScript)遇到了 em dash 或类似字符。请在第 1 行添加 # -*- coding: utf-8 -*-,或替换该字符:em dash 改为 --,箭头改为 ->。同一文件在 rhinocode(CPython 3)下运行正常,这会掩盖该问题。

参考

上游文档

评论

支持 Markdown