Sketchup软件二次开发+Ruby+VisualStudioCode

目录

参考资料:

https://www.bilibili.com/opus/1035200995109371943

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扩展程序的制作


扩展程序的加载



相关推荐
我命由我123451 小时前
Android 开发问题:Duplicate class android.support.v4.app.INotificationSideChannel...
android·java·开发语言·java-ee·android studio·android-studio·android runtime
熬夜敲代码的小N2 小时前
Unity大场景卡顿“急救包”:从诊断到落地的全栈优化方案
java·unity·游戏引擎
坚持学习前端日记2 小时前
容器化中间件的优缺点
java·中间件
黑客老李2 小时前
一次有趣的通杀
java·数据库·mysql
Можно2 小时前
从零开始:Vue 框架安装全指南
前端·javascript·vue.js
季明洵2 小时前
反转字符串、反转字符串II、反转字符串中的单词
java·数据结构·算法·leetcode·字符串
虫小宝2 小时前
查券返利机器人的异步任务调度:Java XXL-Job+Redis实现海量查券请求的分布式任务分发
java·redis·分布式
Mr_Xuhhh2 小时前
C语言字符串与内存操作函数模拟实现详解
java·linux·算法
瑞雪兆丰年兮2 小时前
[从0开始学Java|第十一天]ArrayList
java·开发语言