目录
参考资料:
SURubyDebugger.dll
下载SURubyDebugger.dll,经它放到软件根目录(如何下载去查看参考资料)

VSCode环境配置
安装Ruby扩展

通过VSCode自带功能生成launch.json+tasks.json(能否直接创建没有验证验证过,VSCode的创建过程,查看参考资料)

配置launch.json(用于启动调试)
json
{
"version": "0.2.0",
"configurations": [
{
// 给这个调试配置起的名字,这个名字会在 VSCode 的调试启动菜单中显示,并用于选择具体的调试配置。
"name": "Listen for rdebug-ide",
// 指定调试器类型为 Ruby。这告诉 VSCode 使用适用于 Ruby 的调试器扩展来处理此调试会话。
"type": "Ruby",
// 指定请求类型为 attach。这意味着 VSCode 将尝试连接到一个已经运行的进程(在这个案例中是通过 rdebug-ide 启动的 SketchUp 或其他 Ruby 应用程序)。
"request": "attach",
// 设置工作目录(Current Working Directory)。${workspaceRoot} 是一个变量,代表打开的项目文件夹的根目录。
"cwd": "${workspaceRoot}",
// 远程主机地址。这里设置为 127.0.0.1 表示本地机器。
"remoteHost": "127.0.0.1",
// 远程端口号。需要与你在启动 Ruby 应用程序时指定的监听端口相匹配。此处设置为 6123。
"remotePort": "6123",
// 远程工作空间根目录。通常与 cwd 相同,特别是在本地调试的情况下。它指定了在远程机器上的相对路径如何映射到本地机器上的路径。
"remoteWorkspaceRoot": "${workspaceRoot}"
}
]
}
配置tasks.json(用于启动调试)
json
{
"version": "2.0.0",
"tasks": [
{
// 给这个任务起一个名字,这个名字会在 VSCode 中显示,并用于引用此任务。
"label": "Start SketchUp with Debugging",
// 指定任务类型为 shell,意味着这个任务将在 shell 环境中执行。
"type": "shell",
// Windows 特定的配置。这里启动的是 SketchUp 2023 的 exe 文件,并通过 -rdebug 参数附加调试服务器的启动参数。'ide port=6123 wait' 表示 SketchUp 将启动 Ruby 调试服务器监听在 6123 端口上,并等待调试器连接。
"windows": {
"command": "&'E:/软件/SketchUp/Sketchup2023/SketchUp.exe' -rdebug 'ide port=6123 wait'"
},
// 定义任务分组信息。
"group": {
// 标识该任务属于 build 类型的任务组。
"kind": "build",
// 标识这个任务为默认的 build 任务。设置为 true 后,可以通过快捷键 Ctrl+Shift+B 直接运行这个任务。
"isDefault": true
},
// 配置问题匹配器,用于捕获编译器输出中的错误和警告。这里为空数组表示没有使用问题匹配器。
"problemMatcher": [],
// 提供关于任务的额外细节描述,仅用于显示目的。
"detail": "启动 SketchUp 并等待调试器连接"
}
]
}
代码案例
pluginsLoad.rb
ruby
# 版权所有 (C) 2016 Trimble Inc
# 在 MIT 许可证下发布
# 此示例演示了扩展被 Extension Warehouse 接受所需的模式。
#
# 关键要求:
#
# * 在根 .rb 文件中注册一个 SketchupExtension 实例。
#
# * 不要在根 .rb 文件中加载其他任何内容 --- 它应当仅用于注册。
#
# * 将扩展的所有代码限制在其自己的命名空间(module)内。
# 这意味着不应使用全局变量、全局常量或全局方法。也意味着不应
# 修改 Ruby API 或 SketchUp API。
# 下面这些是需要的,因为我们使用了这些文件中定义的某些方法,而这些
# 文件不会由 SketchUp 自动加载。
#
# * sketchup.rb 提供 `file_loaded?` 和 `file_loaded`。
#
# * extensions.rb 提供 `SketchupExtension` 类。
require 'sketchup.rb'
require 'extensions.rb'
# 为确保你的扩展不会影响其他已安装的扩展,
# 你需要将所有扩展代码放在你自己的模块内。
#
# 我们建议使用如下模式以获得最佳灵活性:
#
# module PublisherName
# module ExtensionName
# # ...
# end
# end
#
# 这样可以将所有扩展放在一个代表开发者/公司的模块下。
module HVTower
# 这里使用 `file_loaded?` 是为了防止扩展被多次注册。
# 当文件被重新加载时(例如在开发中调试或扩展更新等情况)可能会发生重复注册。
#
# `__FILE__` 常量是 Ruby 的"魔法"常量,会返回当前文件的路径字符串。
# 你不一定要在 `file_loaded?` 中使用该常量 ------ 可以使用任何唯一字符串来表示此文件。
# 但 `__FILE__` 非常方便。
unless file_loaded?(__FILE__)
# 在这里定义扩展。两个参数分别是扩展名称和在扩展启用时应加载的文件。
#
# 注意,被加载的文件 (rbs/main) 必须位于与此根文件同名基名的文件夹中。
#
# 另一点需要注意的是我们省略了 .rb 文件扩展名,写成 `rbs/main` 而不是 `rbs/main.rb`。
# SketchUp 足够智能,可以找到文件,这在你以后决定对扩展加密时是必须的。
ex = SketchupExtension.new('批量插入高压塔', 'rbs/High-VoltageTower')
# 接下来我们向扩展添加一些信息。这不是必需的,但强烈推荐,因为它有助于用户管理已安装的扩展。
ex.description = '批量插入高压塔.'
ex.version = '1.0.0'
ex.copyright = 'Trimble Navigations © 2016'
ex.creator = '生产数智化'
# 最后我们告诉 SketchUp 注册此扩展。记住始终将第二个参数设为 true ------ 这会告诉 SketchUp 默认加载该扩展。
# 否则用户在安装后必须手动启用扩展。
Sketchup.register_extension(ex, true)
# 这是加载保护所需的,以防止扩展被多次注册。
file_loaded(__FILE__)
end
end # 模块 Examples 结束
rbs\High-VoltageTower.rb
ruby
# Copyright 2016 Trimble Inc
# Licensed under the MIT license
require 'sketchup.rb'
module HVTower
# 此方法在模型中创建一个简单的立方体并放入一个组内。
def self.create_cube
# 我们需要引用当前激活的模型。SketchUp API 目前只能操作激活的模型。在 Windows 下一次只会有一个模型打开,但在 OS X 下可能会有多个模型打开。
#
# 注意:如果在 OS X 下没有打开模型,`active_model` 将返回 nil。本例为简化处理忽略了这一点。
model = Sketchup.active_model
# 每当对模型进行更改时,必须使用 `model.start_operation` 和 `model.commit_operation` 将所有操作包裹为单个撤销步骤。否则用户可能无法全部撤销,从而丢失工作内容。
#
# 确保对模型的更改可以在单个撤销步骤内撤销,这是 Extension Warehouse 提交质量检查的要求。
#
# 注意:第一个参数是一个字符串,会显示在 编辑 > 撤销 菜单中 ------ 因此请确保为操作命名为用户易懂的内容。
model.start_operation('批量插入高压塔', true)
prompts = ['X 坐标 (m)', 'Y 坐标 (m)', 'Z 坐标 (m)']
defaults = [0.0, 0.0, 0.0]
# 显示输入框
input = UI.inputbox(prompts, defaults, '输入项目基点坐标')
return unless input # 用户点取消
x, y, z = input.map { |v| v.to_f }
basePoint = Geom::Point3d.new(x, y, z)
# 让用户选择一个文件
file_path_json = UI.openpanel('选择一个文件', '', 'Json Files|*.json')#目录不能有中文(中文也可以,但是调试看不到完整路径,并且可能会运行异常)
unless file_path_json && File.exist?(file_path_json)
UI.messagebox("未选择有效文件,请重试!")
return
end # unless
# 让用户选择一个文件
file_path_skp = UI.openpanel('选择一个文件', '', 'Skp Files|*.skp')#目录不能有中文(中文也可以,但是调试看不到完整路径,并且可能会运行异常)
unless file_path_skp && File.exist?(file_path_skp)
UI.messagebox("未选择有效文件,请重试!")
return
end # unless
content = ""
data = nil
# 读取file_path_json文件中的内容
if file_path_json
File.open(file_path_json, 'r:utf-8') do |file|
# 得到文件中所有内容
content = file.read
# 反序列化content
data = JSON.parse(content)
#file.each_line do |line|
#end file.each_line
end # File.open
end # if file_path_json
inch_per_meter = 39.37
# 遍历data中的数据
data.each do |item|
begin
if item["Name"] == "高压塔"
# 通过 API 创建组与通过 UI 略有不同。在 UI 中先创建面然后再将其分组。但通过 API 要先创建组,然后直接向组中添加内容。
group = model.active_entities.add_group
entities = group.entities
# 拿到Location
location = item["Location"]
# 拿到Roatte
rotatte = item["Rotate"]
# 插入skp模型
definition = model.definitions.load(file_path_skp)
dx = (location["X"] - x) * inch_per_meter
dy = (location["Y"] - y) * inch_per_meter
dz = (location["Z"] - z) * inch_per_meter
t1 = Geom::Transformation.translation(Geom::Vector3d.new(dx, dy, dz))
#t1 = Geom::Transformation.translation((location["X"] - x) * inch_per_meter, (location["Y"] - y) * inch_per_meter, (location["Z"] - z) * inch_per_meter)
t2 = Geom::Transformation.rotation(ORIGIN, Z_AXIS, rotatte.degrees)
#t3 = Geom::Transformation.scaling(1.2)
transform = t1 * t2
# 插入实例
instance = entities.add_instance(definition, transform)
elsif item["Name"] == "高压线"
# 拿到Pt1
pt1 = item["Pt1"]
# 拿到Pt2
pt2 = item["Pt2"]
# 路径点(示例:两基塔之间)
pt1x = pt1["X"] - x
pt1y = pt1["Y"] - y
pt1z = pt1["Z"] - z - 0.06
pt2x = pt2["X"] - x
pt2y = pt2["Y"] - y
pt2z = pt2["Z"] - z - 0.06
point1 = Geom::Point3d.new(pt1x * inch_per_meter, pt1y * inch_per_meter, pt1z * inch_per_meter)
point2 = Geom::Point3d.new(pt2x * inch_per_meter, pt2y * inch_per_meter, pt2z * inch_per_meter)
self.create_powerline(point1,point2,5 * inch_per_meter,0.06 * inch_per_meter)
end # if
rescue => exception
puts exception.message
end
end # data.each do |item|
# 最后我们完成操作并提交。在生产环境中你应捕获错误并在失败时中止以进行清理。但为简化示例这里不做这些处理。
model.commit_operation
# 路径点(示例:两基塔之间)
#pt1 = Geom::Point3d.new(0, 0, 1000)
#pt2 = Geom::Point3d.new(1000, 0, 1000)
#self.create_powerline(pt1,pt2,5,0.2)
end
# 创建高压线
def self.create_powerline(p1, p2, sag, radius, segments = 20)
model = Sketchup.active_model
#ents = model.active_entities
model.start_operation("Create Catenary Power Line", true)
group = model.active_entities.add_group
ents = group.entities
# 方向向量
vec = p2 - p1
length = vec.length
dir = vec.normalize
# 构造垂直方向(假设 Z 是竖直)
up = Geom::Vector3d.new(0, 0, 1)
# 中点
mid = p1.offset(dir, length / 2)
# 生成悬链线点
points = []
segments.times do |i|
t = i.to_f / (segments - 1)
x = length * (t - 0.5)
# 抛物线等效悬链(工程常用)
y = -4.0 * sag * (x / length)**2 + sag
base = p1.offset(dir, length * t)
pt = base.offset(up, -y)
points << pt
end
# 画路径
path = []
points.each_cons(2) do |a, b|
path << ents.add_line(a, b)
end
# 在起点画圆截面
normal = dir
#normal = up if normal.length < 0.001
circle_edges = ents.add_circle(points.first, normal, radius, 24)
face = ents.add_face(circle_edges)
# Follow Me
face.followme(path)
# ========== 上色开始 ==========
mat = model.materials['TowerSteel'] ||
model.materials.add('TowerSteel')
mat.color = Sketchup::Color.new(34, 34, 45) # 钢结构灰
group.material = mat
# ========== 上色结束 ==========
model.commit_operation
end
# 在这里为扩展添加一个菜单项。注意这里再次使用加载保护(load guard)以防止意外创建多个菜单项。
unless file_loaded?(__FILE__)
# 获取我们想要添加菜单项的顶级菜单引用。这里使用的是 "Plugins"(旧称),而不是 "Extensions"。使用 "Plugins" 可以保持向后兼容。
menu = UI.menu('Plugins')
# 在本例中我们将菜单项直接添加到菜单根目录。但如果每个扩展要添加多个项,建议将它们分组到子菜单中以便保持组织性。
menu.add_item('批量插入高压塔') {
self.create_cube
}
file_loaded(__FILE__)
end
end # module Examples
Sketchup扩展程序的制作


扩展程序的加载


