ئۇمۇمىي چۈشەنچە概述Overview
بۇ بەت سىزگە ئۆزىڭىزنىڭ قىستۇرما نودى (plugin node) يېزىپ، «يۈگەنلىك ئات» قىستۇرمىسىغا قوشۇشقا ياردەم بېرىدۇ. بىر دانە .rb ھۆججىتى يېزىپ، تەڭشەكتىكى «نود قاچىلاش» بېتىدىن قاچىلسىڭىز بولىدۇ. تۆۋەندە ھەر بىر بۆلۈمنىڭ كود قېلىپى ۋە تەپسىلىي چۈشەندۈرۈشى بېرىلدى.
本助手帮助你编写自己的插件节点,并通过设置中的“安装节点”页面装入草图大师参数化设计。下面给出每个部分的代码模板与详细说明。
This helper lets you author your own plugin node and install it into the Sketchupat3.0 extension via the Settings → Node Install page. Each section below provides a copy-ready code template plus explanation.
قۇرۇلما结构Architecture
ھەر بىر قىستۇرما نودى ئىككى قىسىمدىن تۈزۈلىدۇ: 每个插件节点由两部分组成: Every plugin node has two parts:
NodeRegistry.register(...)بىلەن نودنى جاكارلاش (ئىسمى، تۈرى، ئېغىز/چىقىش، پارامېتىر).用NodeRegistry.register(...)声明节点(名称、类别、输入/输出、参数)。Declare it withNodeRegistry.register(...)(name, category, inputs/outputs, params).PluginLoader.register_compute(...)بىلەن گېئومېترىيە ھاسىل قىلغۇچى فۇنكسىيە (lambda) بېرىش.用PluginLoader.register_compute(...)提供几何生成函数(lambda)。Provide its geometry viaPluginLoader.register_compute(...)(a lambda).
ئەڭ كىچىك قېلىپ最小模板Minimal template
تۆۋەندىكى قېلىپنى كۆچۈرۈپ، key ۋە ئىسىملىرىنى ئۆزىڭىزچە ئالماشتۇرۇپ ئىشلىتىڭ:
复制下面的模板,把 key 和名称替换成你自己的:
Copy the template below and replace key and the names with your own:
module Sketchupat
NodeRegistry.register(
key: 'my_node', # نودنىڭ بىردىنبىر كودى (type بىلەن ئوخشاش)
category: 'plugins', # كۆرسىتىلىدىغان تۈر (plugins = قىستۇرما)
icon: '⭐', # كۆرسەتمە بەلگە (emoji)
source: 'plugin',
name: 'مېنىڭ نودۇم',
author: 'your name',
color: '#f59e0b',
desc: 'قىسقىچە چۈشەندۈرۈش',
inputs: [],
outputs: [{ key: 'out', label: '', type: 'geometry' }],
params: [{ key: 'size', label: 'چوڭلۇقى', type: 'number', def: 100 }]
)
PluginLoader.register_compute('my_node', lambda do |node, nodes_by_id, outputs, model, bake_group, visiting, used_sources|
ents = bake_group.respond_to?(:entities) ? bake_group.entities : model.active_entities
s = ((node['params'] || {})['size'] || 100).to_f
# گېئومېترىيە قۇرۇپ ents غا قوشۇڭ، نەتىجىنى قايتۇرۇڭ
ents.add_face([
Geom::Point3d.new(0, 0, 0),
Geom::Point3d.new(s, 0, 0),
Geom::Point3d.new(s, s, 0),
Geom::Point3d.new(0, s, 0)
])
end)
end
module Sketchupat
NodeRegistry.register(
key: 'my_node', # 节点的唯一代码(与 type 相同)
category: 'plugins', # 所属分类(plugins = 插件)
icon: '⭐', # 显示图标(emoji)
source: 'plugin',
name: '我的节点',
author: 'your name',
color: '#f59e0b',
desc: '简短说明',
inputs: [],
outputs: [{ key: 'out', label: '', type: 'geometry' }],
params: [{ key: 'size', label: '大小', type: 'number', def: 100 }]
)
PluginLoader.register_compute('my_node', lambda do |node, nodes_by_id, outputs, model, bake_group, visiting, used_sources|
ents = bake_group.respond_to?(:entities) ? bake_group.entities : model.active_entities
s = ((node['params'] || {})['size'] || 100).to_f
# 生成几何并加入 ents,返回结果
ents.add_face([
Geom::Point3d.new(0, 0, 0),
Geom::Point3d.new(s, 0, 0),
Geom::Point3d.new(s, s, 0),
Geom::Point3d.new(0, s, 0)
])
end)
end
module Sketchupat
NodeRegistry.register(
key: 'my_node', # unique node code (same as type)
category: 'plugins', # category shown (plugins = plugin)
icon: '⭐', # display icon (emoji)
source: 'plugin',
name: 'My Node',
author: 'your name',
color: '#f59e0b',
desc: 'short description',
inputs: [],
outputs: [{ key: 'out', label: '', type: 'geometry' }],
params: [{ key: 'size', label: 'Size', type: 'number', def: 100 }]
)
PluginLoader.register_compute('my_node', lambda do |node, nodes_by_id, outputs, model, bake_group, visiting, used_sources|
ents = bake_group.respond_to?(:entities) ? bake_group.entities : model.active_entities
s = ((node['params'] || {})['size'] || 100).to_f
# build geometry, add to ents, return it
ents.add_face([
Geom::Point3d.new(0, 0, 0),
Geom::Point3d.new(s, 0, 0),
Geom::Point3d.new(s, s, 0),
Geom::Point3d.new(0, s, 0)
])
end)
end
ئېغىز تىپلىرى端口类型Port types
| تىپ类型Type | مەنىسى含义Meaning | مىسال示例Example |
|---|---|---|
number | سان قىممىتى数值numeric value | def: 10 |
geometry | گېئومېترىيە (جىسىم/سىزىق/يۈز)几何(体/线/面)geometry (body/line/face) | type:'geometry' |
point | بىر دانە نۇقتا单个点single point | type:'point' |
group | گۇرۇپپا/ComponentInstance组/组件group / component | type:'group' |
text | تېكىست كودى文本代码text code | type:'text' |
color | رەڭگى颜色color | type:'color' |
multi: true | بىر ئېغىزغا كۆپ سىم ئۇلاش一个端口允许多根连线allow multiple wires on one port | { key:'pts', type:'geometry', multi:true } |
multi: true بەرىڭ؛ گېئومېترىيە كىرگۈزۈشىگە بىر دانە «نۇقتا/سىزىق» گېئومېترىيەسىنى ئۇلىسىڭىز، ھەر بىر نۇقتىسىغا بىردىن نەرسە قۇيۇلىدۇ.
多连接请设 multi: true;把一条“点/线”几何连到 geometry 输入,即可在每个顶点上生成一个副本。
For multi-link set multi: true; wire one “point/line” geometry into a geometry input and a copy is placed at every vertex.
مىسال: كوردىنات نودى示例:坐标节点Example: Coordinate node
بۇ نود x,y,z پارامېتىرى بويىچە بىر كىچىك بەلگە (گېئومېترىيە) قۇرۇپ چىقىرىدۇ — شۇڭا ئېكراندا كۆرۈنىدۇ.
该节点按 x,y,z 参数生成一个标记(几何),因此能在画面中显示。
This node builds a small marker (geometry) at x,y,z, so it is visible on screen.
module Sketchupat
NodeRegistry.register(
key: 'coord',
category: 'plugins',
icon: '📍',
source: 'plugin',
name: 'كوردىنات',
author: 'your name',
color: '#22c55e',
desc: 'x,y,z دىن بەلگە قۇرۇيدۇ',
inputs: [],
outputs: [{ key: 'out', label: '', type: 'geometry' }],
params: [
{ key: 'x', label: 'X', type: 'number', def: 0 },
{ key: 'y', label: 'Y', type: 'number', def: 0 },
{ key: 'z', label: 'Z', type: 'number', def: 0 }
]
)
PluginLoader.register_compute('coord', lambda do |node, _n, _o, model, bake_group, _v, _u|
p = node['params'] || {}
x = (p['x'] || 0).to_f
y = (p['y'] || 0).to_f
z = (p['z'] || 0).to_f
s = 20.0
ents = bake_group.respond_to?(:entities) ? bake_group.entities : model.active_entities
# x,y,z دا كىچىك تەكشى يۈز (بەلگە) قۇرۇپ قايتۇرۇش
grp = ents.add_group
grp.entities.add_face([
Geom::Point3d.new(x - s, y - s, z),
Geom::Point3d.new(x + s, y - s, z),
Geom::Point3d.new(x + s, y + s, z),
Geom::Point3d.new(x - s, y + s, z)
])
grp
end)
end
module Sketchupat
NodeRegistry.register(
key: 'coord',
category: 'plugins',
icon: '📍',
source: 'plugin',
name: '坐标',
author: 'your name',
color: '#22c55e',
desc: '按 x,y,z 生成标记',
inputs: [],
outputs: [{ key: 'out', label: '', type: 'geometry' }],
params: [
{ key: 'x', label: 'X', type: 'number', def: 0 },
{ key: 'y', label: 'Y', type: 'number', def: 0 },
{ key: 'z', label: 'Z', type: 'number', def: 0 }
]
)
PluginLoader.register_compute('coord', lambda do |node, _n, _o, model, bake_group, _v, _u|
p = node['params'] || {}
x = (p['x'] || 0).to_f
y = (p['y'] || 0).to_f
z = (p['z'] || 0).to_f
s = 20.0
ents = bake_group.respond_to?(:entities) ? bake_group.entities : model.active_entities
# 在 x,y,z 处生成一个平面小标记并返回
grp = ents.add_group
grp.entities.add_face([
Geom::Point3d.new(x - s, y - s, z),
Geom::Point3d.new(x + s, y - s, z),
Geom::Point3d.new(x + s, y + s, z),
Geom::Point3d.new(x - s, y + s, z)
])
grp
end)
end
module Sketchupat
NodeRegistry.register(
key: 'coord',
category: 'plugins',
icon: '📍',
source: 'plugin',
name: 'Coordinate',
author: 'your name',
color: '#22c55e',
desc: 'build a marker at x,y,z',
inputs: [],
outputs: [{ key: 'out', label: '', type: 'geometry' }],
params: [
{ key: 'x', label: 'X', type: 'number', def: 0 },
{ key: 'y', label: 'Y', type: 'number', def: 0 },
{ key: 'z', label: 'Z', type: 'number', def: 0 }
]
)
PluginLoader.register_compute('coord', lambda do |node, _n, _o, model, bake_group, _v, _u|
p = node['params'] || {}
x = (p['x'] || 0).to_f
y = (p['y'] || 0).to_f
z = (p['z'] || 0).to_f
s = 20.0
ents = bake_group.respond_to?(:entities) ? bake_group.entities : model.active_entities
# build a small flat marker at x,y,z and return it
grp = ents.add_group
grp.entities.add_face([
Geom::Point3d.new(x - s, y - s, z),
Geom::Point3d.new(x + s, y - s, z),
Geom::Point3d.new(x + s, y + s, z),
Geom::Point3d.new(x - s, y + s, z)
])
grp
end)
end
مىسال: كۆپ نۇقتا چېچىش (scatter)示例:多点散布Example: Multi-point scatter
بۇ نود بىر geometry كىرگۈزۈشىگە ئۇلانغان گېئومېترىيەنىڭ ھەر بىر نۇقتىسىغا بىردىن قۇتىدىن قۇيىدۇ. مەسىلەن بىر «سىزىق» ياكى «نۇقتا» گېئومېترىيەسىنى ئۇلىسىڭىز، ھەر بىر نۇقتىغا بىر قۇتىدىن پەيدا بولىدۇ.
该节点把连接到一个 geometry 输入的几何体的每一个顶点都放一个方块。例如连一条“线”或“点”几何,每个顶点处都会出现一个方块。
This node places one box at every vertex of the geometry wired into its geometry input. Wire a “line” or “points” geometry and a box appears at each vertex.
module Sketchupat
NodeRegistry.register(
key: 'scatter_box',
category: 'plugins',
icon: '🔳',
source: 'plugin',
name: 'چېچىپ قۇتىلاش',
author: 'your name',
color: '#0ea5e9',
desc: 'گېئومېترىيەنىڭ ھەر بىر نۇقتىسىغا بىردىن قۇتىدىن قۇيىدۇ',
inputs: [{ key: 'geom', label: 'گېئومېترىيە', type: 'geometry' }],
outputs: [{ key: 'out', label: '', type: 'geometry' }],
params: [{ key: 'size', label: 'چوڭلۇقى', type: 'number', def: 50 }]
)
PluginLoader.register_compute('scatter_box', lambda do |node, nodes_by_id, outputs, model, bake_group, visiting, used_sources|
eng = Sketchupat::SketchupatEngine
geom = eng.resolve(node, 'geom', nodes_by_id, outputs, model, bake_group, visiting, used_sources)
return nil unless eng.geometry?(geom)
# گېئومېترىيەنىڭ بارلىق نۇقتىسىنى يىغىش
pts = []
eng.flatten_geom(geom).each do |e|
if e.is_a?(Sketchup::Edge)
pts << e.start if e.start
pts << e.end if e.end
elsif e.respond_to?(:vertices)
e.vertices.each { |vv| pts << vv.position }
end
end
pts.uniq!
return nil if pts.empty?
s = ((node['params'] || {})['size'] || 50).to_f
h = s / 2.0
ents = bake_group.respond_to?(:entities) ? bake_group.entities : model.active_entities
grp = ents.add_group
pts.each do |pt|
box = grp.entities.add_group
box.entities.add_face([
Geom::Point3d.new(pt.x - h, pt.y - h, pt.z - h),
Geom::Point3d.new(pt.x + h, pt.y - h, pt.z - h),
Geom::Point3d.new(pt.x + h, pt.y + h, pt.z - h),
Geom::Point3d.new(pt.x - h, pt.y + h, pt.z - h)
])
end
grp
end)
end
module Sketchupat
NodeRegistry.register(
key: 'scatter_box',
category: 'plugins',
icon: '🔳',
source: 'plugin',
name: '散布方块',
author: 'your name',
color: '#0ea5e9',
desc: '在几何体的每个顶点处放一个方块',
inputs: [{ key: 'geom', label: '几何体', type: 'geometry' }],
outputs: [{ key: 'out', label: '', type: 'geometry' }],
params: [{ key: 'size', label: '大小', type: 'number', def: 50 }]
)
PluginLoader.register_compute('scatter_box', lambda do |node, nodes_by_id, outputs, model, bake_group, visiting, used_sources|
eng = Sketchupat::SketchupatEngine
geom = eng.resolve(node, 'geom', nodes_by_id, outputs, model, bake_group, visiting, used_sources)
return nil unless eng.geometry?(geom)
# 收集几何体的所有顶点
pts = []
eng.flatten_geom(geom).each do |e|
if e.is_a?(Sketchup::Edge)
pts << e.start if e.start
pts << e.end if e.end
elsif e.respond_to?(:vertices)
e.vertices.each { |vv| pts << vv.position }
end
end
pts.uniq!
return nil if pts.empty?
s = ((node['params'] || {})['size'] || 50).to_f
h = s / 2.0
ents = bake_group.respond_to?(:entities) ? bake_group.entities : model.active_entities
grp = ents.add_group
pts.each do |pt|
box = grp.entities.add_group
box.entities.add_face([
Geom::Point3d.new(pt.x - h, pt.y - h, pt.z - h),
Geom::Point3d.new(pt.x + h, pt.y - h, pt.z - h),
Geom::Point3d.new(pt.x + h, pt.y + h, pt.z - h),
Geom::Point3d.new(pt.x - h, pt.y + h, pt.z - h)
])
end
grp
end)
end
module Sketchupat
NodeRegistry.register(
key: 'scatter_box',
category: 'plugins',
icon: '🔳',
source: 'plugin',
name: 'Scatter Box',
author: 'your name',
color: '#0ea5e9',
desc: 'place a box at every vertex of the geometry',
inputs: [{ key: 'geom', label: 'Geometry', type: 'geometry' }],
outputs: [{ key: 'out', label: '', type: 'geometry' }],
params: [{ key: 'size', label: 'Size', type: 'number', def: 50 }]
)
PluginLoader.register_compute('scatter_box', lambda do |node, nodes_by_id, outputs, model, bake_group, visiting, used_sources|
eng = Sketchupat::SketchupatEngine
geom = eng.resolve(node, 'geom', nodes_by_id, outputs, model, bake_group, visiting, used_sources)
return nil unless eng.geometry?(geom)
# gather all vertices of the geometry
pts = []
eng.flatten_geom(geom).each do |e|
if e.is_a?(Sketchup::Edge)
pts << e.start if e.start
pts << e.end if e.end
elsif e.respond_to?(:vertices)
e.vertices.each { |vv| pts << vv.position }
end
end
pts.uniq!
return nil if pts.empty?
s = ((node['params'] || {})['size'] || 50).to_f
h = s / 2.0
ents = bake_group.respond_to?(:entities) ? bake_group.entities : model.active_entities
grp = ents.add_group
pts.each do |pt|
box = grp.entities.add_group
box.entities.add_face([
Geom::Point3d.new(pt.x - h, pt.y - h, pt.z - h),
Geom::Point3d.new(pt.x + h, pt.y - h, pt.z - h),
Geom::Point3d.new(pt.x + h, pt.y + h, pt.z - h),
Geom::Point3d.new(pt.x - h, pt.y + h, pt.z - h)
])
end
grp
end)
end
geom ئېغىزىغا ئۇلاڭ — ھەر بىر نۇقتا بىر قۇتىغا ئايلىنىدۇ.
要散布到多个点:把“点”或“线”节点的输出连到这个 geom 端口,每个点都会生成一个方块。
To scatter across several points: wire a “point” or “line” node's output into this geom port; each point becomes one box.
لوگو تەييارلاش图标准备Logo / icon
نود لوگوسى 200×200 px چوڭلۇقتىكى PNG بولىدۇ. ئىككى ئۇسۇل بار:
节点图标为 200×200 px 的 PNG。有两种方式:
Node icon is a 200×200 px PNG. Two ways:
- (A) base64 بىلەن بىرلەشتۈرۈش — رەسىمنى base64 كودىغا ئايلاندۇرۇپ
icon_data:غا يېزىڭ. بىرلا.rbھۆججەت بولۇپ، لوگو كود بىلەن بىللە يۈرىدۇ.(A) base64 内联 — 把图片转成 base64 写入icon_data:。单个.rb文件,图标随代码一起。(A) inline base64 — convert the image to base64 and put it inicon_data:. A single.rbfile carries both code and icon. - (B) بوغچا ئىچىدە رەسىم —
.rbz(zip) بوغچا ئىچىگە.rbۋە رەسىمنى بىللە قاچىلاڭ. رەسىم ئىسمى نودkeyى بىلەن ئوخشاش بولسۇن (مەسىلەنscatter_box.png).(B) 包内图片 — 把.rb和图片一起打成.rbz(zip)。图片名与节点key相同(如scatter_box.png)。(B) image in package — pack the.rband the image together into a.rbz(zip). The image name matches the nodekey(e.g.scatter_box.png).
ساقلاش ئورنى: icons/nodes/<key>.png. لوگو قىستۇرما قاچلانغاندا ئاپتوماتىك شۇ يەرگە يېزىلىدۇ.
存放路径: icons/nodes/<key>.png。安装插件时图标会自动写入此处。
Stored at: icons/nodes/<key>.png. The icon is written there automatically on install.
قاچلاش بوغچىسى打包安装Build & install
بىرلا .rb بولسا بىۋاسىتە قاچىلاڭ. بىر نەچچە نود + لوگو بولسا .rbz (zip) قىلىپ ئىسىم بېرىڭ:
单个 .rb 可直接安装。多个节点+图标请打成 .rbz(zip):
A single .rb can be installed directly. For several nodes + icons, package as .rbz (zip):
# your_plugin.rbz تەركىبى:
# star.rb
# star.png
# extra_node.rb
#
# قاچىلاش:
# 1) قىستۇرما ئىچىدە «نود قاچىلاش» بېتىنى ئېچىڭ
# 2) «قاچىلا» نى بېسىپ your_plugin.rbz نى تالاڭ
# 3) نود پالىترىدىكى «قىستۇرما» تۈرىگە قوشۇلىدۇ
# your_plugin.rbz 内容:
# star.rb
# star.png
# extra_node.rb
#
# 安装:
# 1) 在插件内打开“安装节点”页面
# 2) 点击“安装”选择 your_plugin.rbz
# 3) 节点会加入面板中的“插件”分类
# contents of your_plugin.rbz:
# star.rb
# star.png
# extra_node.rb
#
# install:
# 1) open the "Node Install" page inside the extension
# 2) click "Install" and pick your_plugin.rbz
# 3) the node is added to the "Plugin" category in the palette
دىققەت نۇقتىلىرى注意事项Important notes
- نودنىڭ
typeقىممىتىkeyبىلەن ئوخشاش بولۇشى كېرەك.节点的type必须与key相同。A node'stypemust equal itskey. - گېئومېترىيە قايتۇرۇشتا چوقۇم
bake_group.entities(ياكىmodel.active_entities) غا قوشۇڭ، نەتىجىنى قايتۇرۇڭ.生成几何时必须加入bake_group.entities(或model.active_entities)并返回结果。When producing geometry, add it tobake_group.entities(ormodel.active_entities) and return it. - كۆپ ئۇلانما ئۈچۈن ئېغىزدا
multi: trueبېرىڭ.多连接入口需设multi: true。For multi-link inputs setmulti: true. - ئىچكى
computeفۇنكسىيەسىnilقايتۇرسا، ئاندىن قىستۇرما (plugin) ئىشلىتىلىدۇ — شۇڭا نودtypeى ئۆزەك تىپلار بىلەن توقۇنۇشماسلىقى كېرەك.只有内置compute返回nil时才会调用插件,所以节点type不要与内置类型冲突。The plugin runs only if the built-incomputereturnsnil, so your nodetypemust not collide with built-in types. - خاتالىق چىقسا نود قىزىل رەڭدە كۆرسىتىلىدۇ — مائۇسنى توختىتىپ سەۋەبىنى كۆرۈڭ.出错时节点显示为红色,悬停鼠标可查看原因。On error the node turns red; hover to see the reason.