├── LICENSE
├── RBFNodes
├── __init__.py
├── changelog.txt
├── core
│ ├── __init__.py
│ ├── dev.py
│ ├── driver.py
│ ├── handler.py
│ ├── matrix.py
│ ├── nodeTree.py
│ ├── plugs.py
│ ├── poses.py
│ ├── properties.py
│ ├── rbf.py
│ ├── shapeKeys.py
│ └── utils.py
├── dev.py
├── editor
│ ├── __init__.py
│ ├── categories.py
│ ├── nodeTree.py
│ └── nodes
│ │ ├── __init__.py
│ │ ├── common.py
│ │ ├── custom_input.py
│ │ ├── custom_output.py
│ │ ├── location_input.py
│ │ ├── location_output.py
│ │ ├── modifier_input.py
│ │ ├── modifier_output.py
│ │ ├── node.py
│ │ ├── node_input.py
│ │ ├── node_output.py
│ │ ├── object_input.py
│ │ ├── object_output.py
│ │ ├── pose.py
│ │ ├── property_input.py
│ │ ├── property_output.py
│ │ ├── rbf.py
│ │ ├── rotation_input.py
│ │ ├── rotation_output.py
│ │ ├── scale_input.py
│ │ ├── scale_output.py
│ │ ├── shapeKey_input.py
│ │ ├── shapeKey_output.py
│ │ └── sockets
│ │ ├── __init__.py
│ │ ├── node_socket.py
│ │ ├── object_socket.py
│ │ ├── pose_socket.py
│ │ ├── property_socket.py
│ │ └── socket.py
├── language.py
├── locales
│ ├── __init__.py
│ ├── strings_de.py
│ ├── strings_en.py
│ ├── strings_es.py
│ ├── strings_fr.py
│ ├── strings_jp.py
│ ├── strings_ko.py
│ ├── strings_pt.py
│ └── strings_zh.py
├── preferences.py
├── ui
│ ├── __init__.py
│ ├── icons.py
│ ├── operators.py
│ └── panel.py
└── var.py
├── README.md
├── pickWalk.py
├── placeReflection
├── __init__.py
└── icons
│ └── placeReflection.png
├── rapidSDK.py
├── smoothWeights
├── __init__.py
├── changelog.txt
├── constants.py
├── dev.py
├── language.py
├── locales
│ ├── __init__.py
│ ├── strings_de.py
│ ├── strings_en.py
│ ├── strings_es.py
│ ├── strings_fr.py
│ ├── strings_jp.py
│ ├── strings_ko.py
│ ├── strings_pt.py
│ └── strings_zh.py
├── panel.py
├── pies.py
├── preferences.py
├── smoothWeights.py
├── symmetryMap.py
├── usage.txt
├── utils.py
└── weights.py
├── thumbMate.py
└── toolShelf
├── __init__.py
├── changelog.txt
├── cheatSheet.png
├── config.py
├── constants.py
├── language.py
├── locales
├── __init__.py
├── strings_de.py
├── strings_en.py
├── strings_es.py
├── strings_fr.py
├── strings_jp.py
├── strings_ko.py
├── strings_pt.py
└── strings_zh.py
├── preferences.py
└── toolShelf.py
/RBFNodes/__init__.py:
--------------------------------------------------------------------------------
1 | """
2 | RBF Nodes
3 | Copyright (C) 2022-2023, Ingo Clemens, brave rabbit, www.braverabbit.com
4 |
5 | GNU GENERAL PUBLIC LICENSE Version 3
6 |
7 | This program is free software: you can redistribute it and/or modify
8 | it under the terms of the GNU General Public License as published by
9 | the Free Software Foundation, either version 3 of the License, or
10 | (at your option) any later version.
11 |
12 | This program is distributed in the hope that it will be useful,
13 | but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 | GNU General Public License for more details.
16 |
17 | You should have received a copy of the GNU General Public License
18 | along with this program.
19 | If not, see .
20 | """
21 |
22 | import bpy
23 |
24 | from . import preferences, editor, ui
25 | from . core import handler, utils
26 |
27 |
28 | bl_info = {"name": "RBF Nodes",
29 | "author": "Ingo Clemens",
30 | "version": (1, 2, 1),
31 | "blender": (3, 1, 0),
32 | "category": "Animation",
33 | "location": "Editors > RBF Nodes Editor",
34 | "description": "Node-based RBF solver for driving properties",
35 | "warning": "",
36 | "doc_url": "https://github.com/IngoClemens/blender/wiki/RBF-Nodes",
37 | "tracker_url": ""}
38 |
39 |
40 | # ----------------------------------------------------------------------
41 | # Registration
42 | # ----------------------------------------------------------------------
43 |
44 | def register():
45 | """Register the add-on.
46 | """
47 | preferences.register()
48 | editor.register()
49 | ui.register()
50 |
51 | utils.VERSION = bl_info["version"]
52 |
53 | bpy.app.handlers.frame_change_post.append(handler.refresh)
54 | bpy.app.handlers.load_post.append(handler.verifyVersion)
55 | if not bpy.app.background:
56 | bpy.app.handlers.depsgraph_update_post.append(handler.refresh)
57 |
58 |
59 | def unregister():
60 | """Unregister the add-on.
61 | """
62 | preferences.unregister()
63 | editor.unregister()
64 | ui.unregister()
65 |
66 | bpy.app.handlers.frame_change_post.remove(handler.refresh)
67 | bpy.app.handlers.load_post.remove(handler.verifyVersion)
68 | if not bpy.app.background:
69 | bpy.app.handlers.depsgraph_update_post.remove(handler.refresh)
70 |
71 |
72 | if __name__ == "__main__":
73 | register()
74 |
--------------------------------------------------------------------------------
/RBFNodes/changelog.txt:
--------------------------------------------------------------------------------
1 | 1.2.1 - 2024-11-26
2 | - Fixed an incompatibility issue with Blender 4.3.
3 |
4 | 1.2.0 - 2024-06-04
5 | - Added a preference setting to set the number of properties which
6 | can be used as drivers or being driven.
7 |
8 | 1.1.0 - 2023-10-17
9 | - Added compatibility with Blender 4.0.
10 | - Added a language selector with 8 languages.
11 | - Added an error message if stored properties don't match the
12 | current version of Blender.
13 |
14 | 1.0.3 - 2023-08-07
15 | - Fixed that shading nodes cannot be linked due to python changes
16 | in Blender 3.6.
17 |
18 | 1.0.2 - 2023-07-03
19 | - Fixed an error related to the driver or driven object missing a
20 | material assignment when linking an input or output node.
21 | - Fixed that geometry nodes with a geometry socket don't get their
22 | value plugs listed.
23 |
24 | 1.0.1 - 2023-06-09
25 | - Fixed wrong unregistering of the PointerProperty.
26 |
27 | 1.0.0 - 2023-06-08
28 | - Because of some necessary changes new RBF setups are not
29 | compatible with older versions of the add-on. A message appears
30 | when loading a scene which contains an older setup and the side
31 | panel of the RBF editor also displays a message.
32 | The setup is updated when the RBF solver gets reset.
33 | - When adding a new pose with additional shape keys default values
34 | are added to existing poses. This works for shape keys only
35 | because a value of zero can be assumed for all other poses. It
36 | does not work with other properties.
37 | - Updated node names to differentiate between inputs and outputs.
38 | - Activating the RBF sets all input and output nodes to
39 | non-editable to prevent changes during the active state.
40 | - Improved the interpolation when using quaternion-based rotation.
41 | - Improved error message when a decomposition error occurs.
42 | - Added a search and replace option in the developer section to
43 | edit driver and driven data for poses.
44 | - Removed quaternion based rotation sub-types.
45 | - Fixed that the quaternion output rotation properties are
46 | depending on the selected euler axes.
47 | - Fixed that shape key drivers remain when resetting the RBF.
48 | - Fixed that the individual shape-key block name of a duplicated
49 | mesh isn't respected when activating the RBF.
50 | - Fixed that the RBF calculation is using driver values with an
51 | offset of one frame when used in an animation.
52 |
53 | 0.4.0 - 2022-07-11
54 | - Improved error messages when the number of storable values gets
55 | exceeded.
56 | - Added an error label to the RBF node when activating the RBF
57 | fails.
58 | - Added a short description to assist extending the number of pose
59 | values.
60 | - Added a constant to define the number of properties to store the
61 | pose and weight values for better proceduralism.
62 | - Fixed: A pose cannot be edited after the RBF has been reset.
63 |
64 | 0.3.0 - 2022-07-06
65 | - Various changes and improvements.
66 | - Added support for object properties and modifiers.
67 | - Added radius presets.
68 |
69 | 0.2.0 - 2022-05-26
70 | - Merged all individual property sockets into one.
71 |
72 | 0.1.0 - 2022-03-24
73 |
--------------------------------------------------------------------------------
/RBFNodes/core/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IngoClemens/blender/9826535aba0a162259c5ba54054fb62e2862ffe5/RBFNodes/core/__init__.py
--------------------------------------------------------------------------------
/RBFNodes/core/dev.py:
--------------------------------------------------------------------------------
1 | #
2 |
3 | import bpy
4 |
5 | from . import nodeTree, poses
6 | from .. import dev
7 |
8 | import json
9 |
10 |
11 | def dumpPose(context):
12 | """Write the pose data of the current node to the command line.
13 |
14 | :param context: The current context.
15 | :type context: bpy.context
16 | """
17 | node = context.active_node
18 | if node and node.bl_idname == "RBFPoseNode":
19 | dev.log("# Pose: {} ({})".format(node.label, node.name))
20 | dev.log("# Driver:")
21 | lines = poses.recallPoseForObject(json.loads(node.driverData), True)
22 | dev.log("\n".join(lines))
23 | dev.log("# Driven:")
24 | lines = poses.recallPoseForObject(json.loads(node.drivenData), True)
25 | dev.log("\n".join(lines))
26 |
27 |
28 | def dumpRBF(context):
29 | """Write the pose weight matrix to the command line.
30 |
31 | :param context: The current context.
32 | :type context: bpy.context
33 | """
34 | rbfNode = nodeTree.getRBFNode(context)
35 | dev.log(rbfNode.getWeightMatrix())
36 | dev.log("# Quaternions have been converted to an exponential map representation.")
37 | dev.log("# Therefore, matrix values differ from the pose values.")
38 |
--------------------------------------------------------------------------------
/RBFNodes/core/handler.py:
--------------------------------------------------------------------------------
1 | #
2 |
3 | import bpy
4 | from bpy.app.handlers import persistent
5 |
6 | from . import nodeTree, rbf, utils
7 | from .. import language
8 |
9 |
10 | # Get the current language.
11 | strings = language.getLanguage()
12 |
13 |
14 | @persistent
15 | def refresh(none):
16 | """Check, if the elapsed time requires an update of the solvers in
17 | the scene.
18 | """
19 | trees = nodeTree.getSceneTrees()
20 | for tree in trees:
21 | nodes = nodeTree.getRBFFromTree(tree)
22 | for rbfNode in nodes:
23 | if rbfNode.active and not rbfNode.mute:
24 | result = rbf.getPoseWeights(rbfNode)
25 | if result is not None:
26 | title = strings.ERROR_EVALUATION
27 | if not bpy.app.background:
28 | utils.displayMessage(title, result, 'ERROR')
29 | else:
30 | print("{} : {}".format(title, result))
31 |
32 | @persistent
33 | def verifyVersion(none):
34 | """Check, if the contained RBF nodes of a file match the current
35 | version.
36 | """
37 | trees = nodeTree.getSceneTrees()
38 | for tree in trees:
39 | nodes = nodeTree.getRBFFromTree(tree)
40 | for rbfNode in nodes:
41 | if not utils.verifyVersion(rbfNode):
42 | title = strings.WARNING_TITLE
43 | message = [strings.INFO_UPDATE_1,
44 | "{} {}".format(strings.INFO_UPDATE_2, utils.versionString(utils.VERSION)),
45 | "{}:".format(strings.INFO_UPDATE_3),
46 | strings.INFO_UPDATE_4,
47 | strings.INFO_UPDATE_5]
48 | if not bpy.app.background:
49 | utils.displayMessage(title, message, 'ERROR')
50 | else:
51 | print("{} : {}".format(title, message))
52 | return
53 |
--------------------------------------------------------------------------------
/RBFNodes/core/plugs.py:
--------------------------------------------------------------------------------
1 | #
2 |
3 | import bpy
4 |
5 |
6 | def getInputNodes(socket, nodeId="", allNodes=False):
7 | """Find and return all nodes of the given bl_idname which are
8 | connected to the given input socket.
9 |
10 | :param socket: The input socket to query.
11 | :type socket: bpy.types.NodeSocket
12 | :param nodeId: The bl_idname string of the node to search for.
13 | If passed an empty string all nodes will be returned.
14 | :type nodeId: str
15 | :param allNodes: Evaluate all inputs.
16 | :type allNodes: bool
17 |
18 | :return: A list with all nodes connected to the input.
19 | :rtype: list(bpy.types.Node)
20 | """
21 | nodes = []
22 | for link in socket.links:
23 | srcNode = link.from_node
24 | # First check for reroutes.
25 | if isinstance(srcNode, bpy.types.NodeReroute):
26 | nodes.extend(getInputNodes(srcNode.inputs[0],
27 | nodeId=nodeId))
28 | elif srcNode.bl_idname == nodeId or nodeId == "":
29 | if not isinstance(srcNode, bpy.types.NodeReroute):
30 | nodes.append(srcNode)
31 | elif allNodes:
32 | for plug in srcNode.inputs:
33 | nodes.extend(getInputNodes(plug,
34 | nodeId=nodeId,
35 | allNodes=allNodes))
36 | return nodes
37 |
38 |
39 | def getOutputNodes(socket, nodeId="", allNodes=False):
40 | """Find and return all nodes of the given bl_idname which are
41 | connected to the given output socket.
42 |
43 | :param socket: The output socket to query.
44 | :type socket: bpy.types.NodeSocket
45 | :param nodeId: The bl_idname string of the node to search for.
46 | :type nodeId: str
47 | :param allNodes: Evaluate all inputs.
48 | :type allNodes: bool
49 |
50 | :return: A list with all nodes connected to the output.
51 | :rtype: list(bpy.types.Node)
52 | """
53 | nodes = []
54 | for link in socket.links:
55 | dstNode = link.to_node
56 | # First check for reroutes.
57 | if isinstance(dstNode, bpy.types.NodeReroute):
58 | nodes.extend(getOutputNodes(dstNode.outputs[0],
59 | nodeId=nodeId))
60 | elif dstNode.bl_idname == nodeId or nodeId == "":
61 | if not isinstance(dstNode, bpy.types.NodeReroute):
62 | nodes.append(dstNode)
63 | elif allNodes:
64 | for plug in dstNode.outputs:
65 | nodes.extend(getOutputNodes(plug,
66 | nodeId=nodeId,
67 | allNodes=allNodes))
68 | return nodes
69 |
70 |
71 | def getObjectFromSocket(socket, source=True):
72 | """Return the object from the object node connected to the given
73 | socket.
74 |
75 | :param socket: The output socket to get the object node from.
76 | :type socket: bpy.types.NodeSocket
77 | :param source: True, if the object can be found on the source node.
78 | :type source: bool
79 |
80 | :return: The object of the connected object node.
81 | :rtype: bpy.types.Object or None
82 | """
83 | if source:
84 | nodes = getInputNodes(socket, "RBFObjectOutputNode")
85 | else:
86 | nodes = getOutputNodes(socket, "RBFObjectInputNode")
87 | if len(nodes):
88 | return nodes[0].getObject()
89 |
--------------------------------------------------------------------------------
/RBFNodes/core/properties.py:
--------------------------------------------------------------------------------
1 | #
2 |
3 | import bpy
4 | import idprop
5 | import mathutils
6 |
7 |
8 | def objectProperties(obj):
9 | """Return a list with the object properties of the given object.
10 |
11 | :param obj: The object to get the properties from.
12 | :type obj: bpy.types.Object
13 |
14 | :return: The list with the object properties.
15 | :rtype: list(str)
16 | """
17 | props = []
18 | if not isinstance(obj, bpy.types.PoseBone):
19 | for prop, value in obj.data.bl_rna.properties.items():
20 | if isinstance(value, bpy.types.FloatProperty):
21 | props.append(prop)
22 | return props
23 |
24 |
25 | def customProperties(obj):
26 | """Return a list with the custom properties of the given object.
27 |
28 | :param obj: The object to get the properties from.
29 | :type obj: bpy.types.Object
30 |
31 | :return: The list with the custom properties.
32 | :rtype: list(str)
33 | """
34 | props = []
35 | for prop in obj.keys():
36 | if prop not in '_RNA_UI' and isinstance(obj[prop], (int, float, list, idprop.types.IDPropertyArray)):
37 | props.append(prop)
38 | return props
39 |
40 |
41 | def nodeProperties(node, fromInput=True):
42 | """Return a list with the node properties of the given object.
43 |
44 | :param node: The node to get the properties from.
45 | :type node: bpy.types.Node or bpy.types.NodeGroup
46 | :param fromInput: True, if the input properties should be returned.
47 | :type fromInput: bool
48 |
49 | :return: The list with the node properties and the socket string as
50 | a tuple.
51 | :rtype: list(tuple(str, str))
52 | """
53 | if fromInput:
54 | plugs = node.inputs
55 | name = "inputs"
56 | else:
57 | plugs = node.outputs
58 | name = "outputs"
59 |
60 | props = []
61 | for i, plug in enumerate(plugs):
62 | if hasattr(plug, "default_value"):
63 | value = plug.default_value
64 | if isinstance(value, (int, float, list, bpy.types.bpy_prop_array)):
65 | props.append((plug.name, "{}[{}]".format(name, i)))
66 | return props
67 |
68 |
69 | def objectModifiers(obj):
70 | """Return a list with the modifiers of the given object.
71 |
72 | :param obj: The object to get the modifiers from.
73 | :type obj: bpy.types.Object
74 |
75 | :return: The list with the object modifiers.
76 | :rtype: list(str)
77 | """
78 | mods = []
79 | for mod in obj.modifiers:
80 | mods.append(mod.name)
81 | return mods
82 |
83 |
84 | def modifierProperties(modifier):
85 | """Return a list with the modifier properties of the given object.
86 |
87 | :param modifier: The modifier to get the properties from.
88 | :type modifier: bpy.types.Modifier
89 |
90 | :return: The list with the modifier properties.
91 | :rtype: list(str)
92 | """
93 | props = []
94 | for prop, value in modifier.bl_rna.properties.items():
95 | if isinstance(value, bpy.types.FloatProperty):
96 | props.append(prop)
97 | return props
98 |
99 |
100 | def expandObjectProperty(obj, name):
101 | """Return the name of the property or names in case of an array
102 | property.
103 |
104 | :param obj: The object the property belongs to.
105 | :type obj: bpy.types.Object
106 | :param name: The name of the property
107 | :type name: str
108 |
109 | :return: A list with the property or property items.
110 | :rtype: list(str)
111 | """
112 | value = eval("obj.data.{}".format(name))
113 | if isinstance(value, mathutils.Color):
114 | return [('rna_property:{}[{}]'.format(name, i), value[i], None)
115 | for i in range(len(value))]
116 | else:
117 | return [('rna_property:{}'.format(name), value, None)]
118 |
119 |
120 | def expandProperty(obj, name):
121 | """Return the name of the property or names in case of an array
122 | property.
123 |
124 | :param obj: The object the property belongs to.
125 | :type obj: bpy.types.Object
126 | :param name: The name of the property
127 | :type name: str
128 |
129 | :return: A list with the property or property items.
130 | :rtype: list(str)
131 | """
132 | if name in customProperties(obj):
133 | value = eval("obj['{}']".format(name))
134 | if isinstance(value, idprop.types.IDPropertyArray):
135 | return [('property:["{}"][{}]'.format(name, i), value[i], None)
136 | for i in range(len(value))]
137 | else:
138 | return [('property:["{}"]'.format(name), value, None)]
139 | else:
140 | return []
141 |
142 |
143 | def expandNodeProperty(nodeString, plugString):
144 | """Return the name of the property or names in case of an array
145 | property.
146 |
147 | :param nodeString: The node as a string.
148 | :type nodeString: str
149 | :param plugString: The string representing the input or output
150 | including the index.
151 | :type plugString: str
152 |
153 | :return: A list with the property or property items.
154 | :rtype: list(str)
155 | """
156 | plug = eval(".".join((nodeString, plugString)))
157 | value = plug.default_value
158 | if isinstance(value, bpy.types.bpy_prop_array):
159 | # In general an array property would include the index ([j]) at
160 | # the end but this causes issues when creating or removing
161 | # drivers, since the index needs to be passed separately.
162 | # Therefore, adding the index would be mainly for readability
163 | # when exposing the data but wouldn't have any practical use.
164 | return [('{}:{}.default_value'.format(nodeString, plugString), value[j], None)
165 | for j in range(len(value))]
166 | else:
167 | return [('{}:{}.default_value'.format(nodeString, plugString), value, None)]
168 |
--------------------------------------------------------------------------------
/RBFNodes/core/shapeKeys.py:
--------------------------------------------------------------------------------
1 | #
2 |
3 | import bpy
4 |
5 |
6 | def hasShapeKeys(obj):
7 | """Return, if the given object has shape keys.
8 |
9 | :param obj: The object to query.
10 | :type obj: bpy.types.Object
11 |
12 | :return: True, if the object has shape keys.
13 | :rtype: bool
14 | """
15 | if obj.id_data.type in ['MESH', 'CURVE']:
16 | return True if obj.data.shape_keys else False
17 | else:
18 | return False
19 |
20 |
21 | def shapeKeyName(obj):
22 | """Return the name of the shape key data block if the object has
23 | shape keys.
24 |
25 | :param obj: The mesh or curve object to query.
26 | :type obj: bpy.types.Object
27 |
28 | :return: The name of the shape key data block.
29 | :rtype: str
30 | """
31 | if hasShapeKeys(obj):
32 | return obj.data.shape_keys.name
33 | return ""
34 |
35 |
36 | def shapeKeyProperties(obj):
37 | """Get the names of all shape key properties.
38 |
39 | :param obj: The mesh or curve object to query.
40 | :type obj: bpy.types.Object
41 |
42 | :return: A dictionary with all property names of the shape keys.
43 | :rtype: dict()
44 | """
45 | props = {}
46 | if hasShapeKeys(obj):
47 | for prop in obj.data.shape_keys.key_blocks:
48 | props[prop.name] = prop.value
49 | return props
50 |
--------------------------------------------------------------------------------
/RBFNodes/core/utils.py:
--------------------------------------------------------------------------------
1 | #
2 |
3 | import bpy
4 |
5 | from .. import preferences, var
6 |
7 | import json
8 | import math
9 |
10 |
11 | VERSION = None
12 |
13 |
14 | def verifyVersion(node):
15 | """Check the node version against the version of the add-on.
16 |
17 | :param node: The node.
18 | :type node: bpy.types.Node or None
19 |
20 | :return: True, if the version is valid.
21 | :rtype: bool
22 | """
23 | if node.version is None or not len(node.version):
24 | return False
25 | versionList = json.loads(node.version)
26 | if versionList is None:
27 | return False
28 | return versionList[0] <= VERSION[0]
29 |
30 |
31 | def setVersion(node):
32 | """Set the node version.
33 |
34 | :param node: The node.
35 | :type node: bpy.types.Node
36 | """
37 | node.version = json.dumps(VERSION)
38 |
39 |
40 | def getVersion(node):
41 | """Return the node version as a string.
42 |
43 | :param node: The node.
44 | :type node: bpy.types.Node
45 |
46 | :return: The version string.
47 | :rtype: str
48 | """
49 | versionList = json.loads(node.version)
50 | return versionString(versionList)
51 |
52 |
53 | def versionString(version):
54 | """Return the given version tuple as a string.
55 |
56 | :param version: The version tuple.
57 | :type version: tuple
58 |
59 | :return: The version string.
60 | :rtype: str
61 | """
62 | return ".".join([str(i) for i in version])
63 |
64 |
65 | def displayMessage(title="", message="", icon='INFO'):
66 | """Display an info message window.
67 |
68 | :param title: The title of the window.
69 | :type title: str
70 | :param message: The message to display. This can also be a list of
71 | multiple lines.
72 | :type message: str or list(str)
73 | :param icon: The icon to display.
74 | :type icon: str
75 | """
76 | if not isinstance(message, list):
77 | message = [message]
78 |
79 | def draw(self, context):
80 | """Draw the content of the window.
81 |
82 | :param context: The current context.
83 | :type context: bpy.context
84 | """
85 | for line in message:
86 | self.layout.label(text=line)
87 |
88 | bpy.context.window_manager.popup_menu(draw, title=title, icon=icon)
89 |
90 |
91 | def getMaxSize():
92 | """Return the maximum number of values.
93 |
94 | :return: The maximum number of values for poses and weights.
95 | :rtype: int
96 | """
97 | return preferences.getPreferences().propertyCount * var.MAX_LEN
98 |
99 |
100 | def getArrayCount():
101 | """
102 | """
103 | config = preferences.readConfig()
104 |
105 | if "propertyCount" in config:
106 | return math.ceil(config["propertyCount"] / var.MAX_LEN)
107 | else:
108 | return var.NUM_ARRAYS
109 |
--------------------------------------------------------------------------------
/RBFNodes/dev.py:
--------------------------------------------------------------------------------
1 | #
2 |
3 | import bpy
4 |
5 | import importlib
6 | import os
7 |
8 | from . import preferences
9 |
10 |
11 | def reload(name=""):
12 | """Reload all modules.
13 |
14 | :param name: The name of the submodule to reload only or empty to
15 | reload the entire package.
16 | :type name: str
17 | """
18 | path = os.path.dirname(__file__)
19 | base = os.path.dirname(path)
20 | if len(name):
21 | path = os.path.join(path, name)
22 |
23 | for dirPath, dirs, fileList in os.walk(path):
24 | # Get the folder name from the current path.
25 | dirName = os.path.basename(dirPath)
26 | if dirName not in ["__pycache__"]:
27 | # Build the module name from the current path and strip away
28 | # the base path.
29 | # Replace the path separators with periods.
30 | modName = dirPath.replace(base, "").lstrip(os.sep).replace(os.sep, ".")
31 | for fileItem in fileList:
32 | if fileItem.endswith(".py"):
33 | # Reload the package.
34 | if fileItem == "__init__.py":
35 | moduleName = modName
36 | # Reload the module.
37 | else:
38 | moduleName = ".".join([modName, fileItem[:-3]])
39 | try:
40 | module = __import__(moduleName, fromlist=[""])
41 | importlib.reload(module)
42 | print("Reloading module: {}".format(moduleName))
43 | except Exception as exception:
44 | print(str(exception))
45 |
46 |
47 | # ----------------------------------------------------------------------
48 | # Logging
49 | # ----------------------------------------------------------------------
50 |
51 | def logEnabled():
52 | """Return if logging is enabled when developer mode is active.
53 |
54 | :return: True, if logging is enabled.
55 | :rtype: bool
56 | """
57 | return preferences.getPreferences().developerMode and preferences.getPreferences().logData
58 |
59 |
60 | def log(message):
61 | """Write to the given message to the logger.
62 |
63 | :param message: The message to log.
64 | :type message: str
65 | """
66 | if logEnabled():
67 | print(message)
68 |
69 |
70 | # ----------------------------------------------------------------------
71 | # Stats
72 | # ----------------------------------------------------------------------
73 |
74 | def countLines():
75 | """Print statistics about all files contained in the package
76 | regarding lines and code lines.
77 | """
78 | print("{:>10} |{:>10} |{:>10} |{:>10} |{:>10} | {:<20}".format("Lines", "Code", "%", "Total Code", "All", "File"))
79 | print("{:->11}|{:->11}|{:->11}|{:->11}|{:->11}|{:->20}".format("", "", "", "", "", ""))
80 |
81 | numLines = 0
82 | allLines = 0
83 | for root, dirs, fileList in os.walk(os.path.dirname(__file__)):
84 | for fileItem in fileList:
85 | if fileItem.endswith(".py"):
86 | filePath = "{}/{}".format(root, fileItem)
87 | with open(filePath, "r") as fileObj:
88 | data = fileObj.readlines()
89 | if len(data):
90 | codeLines = _numCodeLines(data)
91 | numLines += codeLines
92 | allLines += len(data)
93 | print("{:>10} |{:>10} |{:>10} |{:>10} |{:>10} | {:<20}".format(len(data),
94 | codeLines,
95 | int((float(codeLines)/len(data))*100.0),
96 | numLines,
97 | allLines,
98 | "{}/{}".format(os.path.basename(root), fileItem)))
99 |
100 |
101 | def _numCodeLines(data):
102 | """Return the number of code lines contained in the given file
103 | data.
104 |
105 | :param data: The content of the file to inspect.
106 | :type data: list(str)
107 |
108 | :return: The number of code lines without comments.
109 | :rtype: int
110 | """
111 | startDoc = False
112 | add = True
113 | numLines = 0
114 | for i in range(len(data)):
115 | if not startDoc:
116 | add = True
117 | line = data[i].lstrip(" ").lstrip("\n")
118 | if line.startswith("#") or not len(line):
119 | add = False
120 | elif '"""' in line or "'''" in line:
121 | add = False
122 | if startDoc or line.count('"""') == 2 or line.count("'''") == 2:
123 | startDoc = False
124 | else:
125 | startDoc = True
126 | if add:
127 | numLines += 1
128 | return numLines
129 |
--------------------------------------------------------------------------------
/RBFNodes/editor/__init__.py:
--------------------------------------------------------------------------------
1 | #
2 |
3 | import bpy
4 |
5 | from . import categories, nodes, nodeTree
6 | from . nodes import sockets
7 |
8 |
9 | # ----------------------------------------------------------------------
10 | # Registration
11 | # ----------------------------------------------------------------------
12 |
13 | # Collect all classes in a list for easier access.
14 | classes = [nodeTree.RBFNodesNodeTree]
15 |
16 |
17 | def register():
18 | """Register the add-on.
19 | """
20 | for cls in classes:
21 | bpy.utils.register_class(cls)
22 |
23 | sockets.register()
24 | nodes.register()
25 |
26 | # Register the node categories.
27 | categories.register()
28 |
29 |
30 | def unregister():
31 | """Unregister the add-on.
32 | """
33 | # Unregister the node categories.
34 | # This needs to be done before the add-on.
35 | categories.unregister()
36 |
37 | for cls in classes:
38 | bpy.utils.unregister_class(cls)
39 |
40 | sockets.unregister()
41 | nodes.unregister()
42 |
--------------------------------------------------------------------------------
/RBFNodes/editor/categories.py:
--------------------------------------------------------------------------------
1 | #
2 |
3 | import bpy
4 | import nodeitems_utils
5 | from nodeitems_utils import NodeCategory, NodeItem
6 |
7 | from .. import language, var
8 |
9 |
10 | # Get the current language.
11 | strings = language.getLanguage()
12 |
13 |
14 | class RBFNodesNodeCategory(NodeCategory):
15 | """Class to define which node tree the RBF categories belong to.
16 | """
17 | @classmethod
18 | def poll(cls, context):
19 | return context.space_data.tree_type == var.NODE_TREE_TYPE
20 |
21 |
22 | # Define the categories.
23 | categories = [RBFNodesNodeCategory('INPUT',
24 | strings.INPUT_LABEL,
25 | items=[NodeItem("RBFObjectInputNode"),
26 | NodeItem("RBFLocationInputNode"),
27 | NodeItem("RBFRotationInputNode"),
28 | NodeItem("RBFScaleInputNode"),
29 | NodeItem("RBFPropertyInputNode"),
30 | NodeItem("RBFCustomInputNode"),
31 | NodeItem("RBFShapeKeyInputNode"),
32 | NodeItem("RBFModifierInputNode"),
33 | NodeItem("RBFNodeInputNode")]),
34 | RBFNodesNodeCategory('OUTPUT',
35 | strings.OUTPUT_LABEL,
36 | items=[NodeItem("RBFObjectOutputNode"),
37 | NodeItem("RBFLocationOutputNode"),
38 | NodeItem("RBFRotationOutputNode"),
39 | NodeItem("RBFScaleOutputNode"),
40 | NodeItem("RBFPropertyOutputNode"),
41 | NodeItem("RBFCustomOutputNode"),
42 | NodeItem("RBFShapeKeyOutputNode"),
43 | NodeItem("RBFModifierOutputNode"),
44 | NodeItem("RBFNodeOutputNode")]),
45 | RBFNodesNodeCategory('RBF',
46 | strings.RBF_LABEL,
47 | items=[NodeItem("RBFSolverNode")])]
48 |
49 |
50 | # ----------------------------------------------------------------------
51 | # Registration
52 | # ----------------------------------------------------------------------
53 |
54 | def register():
55 | """Register the node categories.
56 | """
57 | nodeitems_utils.register_node_categories(var.CATEGORIES_ID, categories)
58 |
59 |
60 | def unregister():
61 | """Unregister the node categories.
62 |
63 | These need to be unregistered before unregistering the add-on.
64 | """
65 | nodeitems_utils.unregister_node_categories(var.CATEGORIES_ID)
66 |
--------------------------------------------------------------------------------
/RBFNodes/editor/nodeTree.py:
--------------------------------------------------------------------------------
1 | #
2 |
3 | import bpy
4 |
5 | from .. import var
6 | from .. import language
7 |
8 |
9 | # Get the current language.
10 | strings = language.getLanguage()
11 |
12 |
13 | class RBFNodesNodeTree(bpy.types.NodeTree):
14 | """RBF Nodes"""
15 | bl_idname = var.NODE_TREE_TYPE
16 | bl_label = strings.EDITOR_LABEL
17 | bl_icon = 'POINTCLOUD_DATA'
18 |
--------------------------------------------------------------------------------
/RBFNodes/editor/nodes/__init__.py:
--------------------------------------------------------------------------------
1 | #
2 |
3 | import bpy
4 |
5 | from . object_input import RBFObjectInputNode
6 | from . object_output import RBFObjectOutputNode
7 | from . pose import RBFPoseNode
8 | from . custom_input import RBFCustomInputNode
9 | from . custom_output import RBFCustomOutputNode
10 | from . property_input import RBFPropertyInputNode
11 | from . property_output import RBFPropertyOutputNode
12 | from . location_input import RBFLocationInputNode
13 | from . location_output import RBFLocationOutputNode
14 | from . rotation_input import RBFRotationInputNode
15 | from . rotation_output import RBFRotationOutputNode
16 | from . scale_input import RBFScaleInputNode
17 | from . scale_output import RBFScaleOutputNode
18 | from . shapeKey_input import RBFShapeKeyInputNode
19 | from . shapeKey_output import RBFShapeKeyOutputNode
20 | from . modifier_input import RBFModifierInputNode
21 | from . modifier_output import RBFModifierOutputNode
22 | from . node_input import RBFNodeInputNode
23 | from . node_output import RBFNodeOutputNode
24 | from . rbf import RBFSolverNode
25 |
26 |
27 | # ----------------------------------------------------------------------
28 | # Registration
29 | # ----------------------------------------------------------------------
30 |
31 | # Collect all classes in a list for easier access.
32 | classes = [RBFObjectInputNode,
33 | RBFObjectOutputNode,
34 | RBFPoseNode,
35 | RBFCustomInputNode,
36 | RBFCustomOutputNode,
37 | RBFPropertyInputNode,
38 | RBFPropertyOutputNode,
39 | RBFLocationInputNode,
40 | RBFLocationOutputNode,
41 | RBFRotationInputNode,
42 | RBFRotationOutputNode,
43 | RBFScaleInputNode,
44 | RBFScaleOutputNode,
45 | RBFShapeKeyInputNode,
46 | RBFShapeKeyOutputNode,
47 | RBFModifierInputNode,
48 | RBFModifierOutputNode,
49 | RBFNodeInputNode,
50 | RBFNodeOutputNode,
51 | RBFSolverNode]
52 |
53 |
54 | def register():
55 | """Register the node classes.
56 | """
57 | for cls in classes:
58 | bpy.utils.register_class(cls)
59 |
60 |
61 | def unregister():
62 | """Unregister the node classes.
63 | """
64 | for cls in classes:
65 | bpy.utils.unregister_class(cls)
66 |
--------------------------------------------------------------------------------
/RBFNodes/editor/nodes/custom_input.py:
--------------------------------------------------------------------------------
1 | #
2 |
3 | import bpy
4 |
5 | from . import common, node
6 | from ... import language
7 |
8 |
9 | # Get the current language.
10 | strings = language.getLanguage()
11 |
12 |
13 | class RBFCustomInputNode(node.RBFNode):
14 | """Custom property input node.
15 | """
16 | bl_idname = "RBFCustomInputNode"
17 | bl_label = strings.CUSTOM_INPUT_LABEL
18 | bl_icon = 'EMPTY_SINGLE_ARROW'
19 |
20 | # ------------------------------------------------------------------
21 | # Property callbacks
22 | # ------------------------------------------------------------------
23 |
24 | def setLabelCallback(self, context):
25 | """Callback for updating the node label based on the property
26 | selection.
27 |
28 | :param context: The current context.
29 | :type context: bpy.context
30 | """
31 | common.customLabelCallback(self)
32 |
33 | def propItems(self, context):
34 | """Callback for the property drop down menu to collect the names
35 | of all custom properties of the connected object.
36 |
37 | :param context: The current context.
38 | :type context: bpy.context
39 |
40 | :return: A list with tuple items for the enum property.
41 | :rtype: list(tuple(str))
42 | """
43 | return common.customItemsCallback(self, source=False)
44 |
45 | # ------------------------------------------------------------------
46 | # Properties
47 | # ------------------------------------------------------------------
48 |
49 | modeItems = [('LIST', strings.AUTO_LABEL, ""),
50 | ('MANUAL', strings.MANUAL_LABEL, "")]
51 | mode : bpy.props.EnumProperty(items=modeItems)
52 | propertyName : bpy.props.StringProperty(name="", update=setLabelCallback)
53 | propertyEnum : bpy.props.EnumProperty(name="", items=propItems, update=setLabelCallback)
54 |
55 | def init(self, context):
56 | """Initialize the node and add the sockets.
57 |
58 | :param context: The current context.
59 | :type context: bpy.context
60 | """
61 | self.addOutput("RBFPropertySocket", strings.CUSTOM_LABEL)
62 |
63 | def draw(self, context, layout):
64 | """Draw the content of the node.
65 |
66 | :param context: The current context.
67 | :type context: bpy.context
68 | :param layout: The current layout.
69 | :type layout: bpy.types.UILayout
70 | """
71 | common.drawCustomProperties(self, layout)
72 |
73 | def draw_buttons_ext(self, context, layout):
74 | """Draw node buttons in the sidebar.
75 |
76 | :param context: The current context.
77 | :type context: bpy.context
78 | :param layout: The current layout.
79 | :type layout: bpy.types.UILayout
80 | """
81 | self.draw(context, layout)
82 |
83 | # ------------------------------------------------------------------
84 | # Getter
85 | # ------------------------------------------------------------------
86 |
87 | def getProperties(self, obj):
88 | """Return the name of the selected custom property.
89 |
90 | :param obj: The object to query.
91 | :type obj: bpy.types.Object
92 |
93 | :return: A list with the selected custom property and the value
94 | as a tuple.
95 | :rtype: list(tuple(str, float))
96 | """
97 | return common.getCustomProperties(self, obj)
98 |
--------------------------------------------------------------------------------
/RBFNodes/editor/nodes/location_input.py:
--------------------------------------------------------------------------------
1 | #
2 |
3 | import bpy
4 |
5 | from . import common, node
6 | from ... import language
7 |
8 |
9 | # Get the current language.
10 | strings = language.getLanguage()
11 |
12 |
13 | class RBFLocationInputNode(node.RBFNode):
14 | """Object location input node.
15 | """
16 | bl_idname = "RBFLocationInputNode"
17 | bl_label = strings.LOCATION_INPUT_LABEL
18 | bl_icon = 'ORIENTATION_LOCAL'
19 |
20 | # ------------------------------------------------------------------
21 | # Properties
22 | # ------------------------------------------------------------------
23 |
24 | x_axis : bpy.props.BoolProperty(name="X", default=False)
25 | y_axis : bpy.props.BoolProperty(name="Y", default=False)
26 | z_axis : bpy.props.BoolProperty(name="Z", default=False)
27 |
28 | def init(self, context):
29 | """Initialize the node and add the sockets.
30 |
31 | :param context: The current context.
32 | :type context: bpy.context
33 | """
34 | self.addOutput("RBFPropertySocket", strings.LOCATION_LABEL)
35 |
36 | def draw(self, context, layout):
37 | """Draw the content of the node.
38 |
39 | :param context: The current context.
40 | :type context: bpy.context
41 | :param layout: The current layout.
42 | :type layout: bpy.types.UILayout
43 | """
44 | common.drawTransformProperties(self, layout)
45 |
46 | def draw_buttons_ext(self, context, layout):
47 | """Draw node buttons in the sidebar.
48 |
49 | :param context: The current context.
50 | :type context: bpy.context
51 | :param layout: The current layout.
52 | :type layout: bpy.types.UILayout
53 | """
54 | self.draw(context, layout)
55 |
56 | # ------------------------------------------------------------------
57 | # Getter
58 | # ------------------------------------------------------------------
59 |
60 | def getProperties(self, obj):
61 | """Return the selected location properties for the given object.
62 |
63 | :param obj: The object to query.
64 | :type obj: bpy.types.Object
65 |
66 | :return: A list with the selected location properties and their
67 | values as a tuple.
68 | :rtype: list(tuple(str, float))
69 | """
70 | return common.getLocationProperties(self, obj)
71 |
--------------------------------------------------------------------------------
/RBFNodes/editor/nodes/location_output.py:
--------------------------------------------------------------------------------
1 | #
2 |
3 | import bpy
4 |
5 | from . import common, node
6 | from ... import language, preferences
7 | from ... core import driver
8 |
9 |
10 | # Get the current language.
11 | strings = language.getLanguage()
12 |
13 |
14 | class RBFLocationOutputNode(node.RBFNode):
15 | """Object location output node.
16 | """
17 | bl_idname = "RBFLocationOutputNode"
18 | bl_label = strings.LOCATION_OUTPUT_LABEL
19 | bl_icon = 'ORIENTATION_LOCAL'
20 |
21 | # ------------------------------------------------------------------
22 | # Properties
23 | # ------------------------------------------------------------------
24 |
25 | def updateCallback(self, context):
26 | """Callback for any value changes.
27 |
28 | :param context: The current context.
29 | :type context: bpy.context
30 | """
31 | pass
32 |
33 | x_axis : bpy.props.BoolProperty(name=strings.X_LABEL, default=False)
34 | y_axis : bpy.props.BoolProperty(name=strings.Y_LABEL, default=False)
35 | z_axis : bpy.props.BoolProperty(name=strings.Z_LABEL, default=False)
36 |
37 | output : bpy.props.FloatVectorProperty(update=updateCallback)
38 | # The indices of the created drivers on the driven object.
39 | driverIndex : bpy.props.IntVectorProperty(default=(-1, -1, -1))
40 | isDriver : bpy.props.BoolProperty(default=False)
41 |
42 | def init(self, context):
43 | """Initialize the node and add the sockets.
44 |
45 | :param context: The current context.
46 | :type context: bpy.context
47 | """
48 | self.addInput("RBFPropertySocket", strings.LOCATION_LABEL)
49 |
50 | def draw(self, context, layout):
51 | """Draw the content of the node.
52 |
53 | :param context: The current context.
54 | :type context: bpy.context
55 | :param layout: The current layout.
56 | :type layout: bpy.types.UILayout
57 | """
58 | common.drawTransformProperties(self, layout)
59 |
60 | if preferences.getPreferences().developerMode:
61 | col = layout.column(align=True)
62 | col.prop(self, "output")
63 |
64 | def draw_buttons_ext(self, context, layout):
65 | """Draw node buttons in the sidebar.
66 |
67 | :param context: The current context.
68 | :type context: bpy.context
69 | :param layout: The current layout.
70 | :type layout: bpy.types.UILayout
71 | """
72 | self.draw(context, layout)
73 |
74 | # ------------------------------------------------------------------
75 | # Getter
76 | # ------------------------------------------------------------------
77 |
78 | def getProperties(self, obj):
79 | """Return the selected location properties for the given object.
80 |
81 | :param obj: The object to query.
82 | :type obj: bpy.types.Object
83 |
84 | :return: A list with the selected location properties and their
85 | values as a tuple.
86 | :rtype: list(tuple(str, float))
87 | """
88 | return common.getLocationProperties(self, obj)
89 |
90 | def getOutputProperties(self):
91 | """Return the selected output properties.
92 |
93 | :return: A list with the node and the selected output property
94 | indices as a tuple.
95 | :rtype: list(bpy.types.Node, int)
96 | """
97 | return common.getTransformOutputProperties(self)
98 |
99 | # ------------------------------------------------------------------
100 | # Driver
101 | # ------------------------------------------------------------------
102 |
103 | def createDriver(self, nodeGroup, driven, rbfNode):
104 | """Create a driver for each selected axis of the driven object.
105 |
106 | :param nodeGroup: The node tree of the current RBF setup.
107 | :type nodeGroup: bpy.types.NodeGroup
108 | :param driven: The driven object.
109 | :type driven: bpy.types.Object
110 | :param rbfNode: The current RBF node.
111 | :type rbfNode: bpy.types.Node
112 | """
113 | driver.createTransformDriver(self, nodeGroup, driven, "location", rbfNode)
114 |
115 | def deleteDriver(self, obj):
116 | """Delete the driver for the given object.
117 |
118 | :param obj: The driven object.
119 | :type obj: bpy.types.Object
120 | """
121 | driver.deleteTransformDriver(self, obj, "location")
122 |
123 | def enableDriver(self, obj, enable):
124 | """Enable or disable the driver FCurves for the given object.
125 |
126 | :param obj: The driven object.
127 | :type obj: bpy.types.Object
128 | :param enable: The enabled state of the driver FCurves.
129 | :type enable: bool
130 | """
131 | driver.enableDriver(self, obj, enable)
132 |
--------------------------------------------------------------------------------
/RBFNodes/editor/nodes/modifier_input.py:
--------------------------------------------------------------------------------
1 | #
2 |
3 | import bpy
4 |
5 | from . import common, node
6 | from ... import language
7 |
8 |
9 | # Get the current language.
10 | strings = language.getLanguage()
11 |
12 |
13 | class RBFModifierInputNode(node.RBFNode):
14 | """Object modifier input node.
15 | """
16 | bl_idname = "RBFModifierInputNode"
17 | bl_label = strings.MODIFIER_INPUT_LABEL
18 | bl_icon = 'MODIFIER'
19 |
20 | # ------------------------------------------------------------------
21 | # Property callbacks
22 | # ------------------------------------------------------------------
23 |
24 | def setLabelCallback(self, context):
25 | """Callback for updating the node label based on the property
26 | selection.
27 |
28 | :param context: The current context.
29 | :type context: bpy.context
30 | """
31 | common.modifierLabelCallback(self)
32 |
33 | def modItems(self, context):
34 | """Callback for the modifier drop down menu to collect the names
35 | of all object modifiers of the connected object.
36 |
37 | :param context: The current context.
38 | :type context: bpy.context
39 |
40 | :return: A list with tuple items for the enum property.
41 | :rtype: list(tuple(str))
42 | """
43 | return common.modifierItemsCallback(self, source=False)
44 |
45 | def propItems(self, context):
46 | """Callback for the property drop down menu to collect the names
47 | of all modifier properties of the selected modifier.
48 |
49 | :param context: The current context.
50 | :type context: bpy.context
51 |
52 | :return: A list with tuple items for the enum property.
53 | :rtype: list(tuple(str))
54 | """
55 | return common.modifierPropertiesCallback(self, source=False)
56 |
57 | # ------------------------------------------------------------------
58 | # Properties
59 | # ------------------------------------------------------------------
60 |
61 | modifierEnum : bpy.props.EnumProperty(name="", items=modItems, update=setLabelCallback)
62 | propertyEnum : bpy.props.EnumProperty(name="", items=propItems, update=setLabelCallback)
63 |
64 | def init(self, context):
65 | """Initialize the node and add the sockets.
66 |
67 | :param context: The current context.
68 | :type context: bpy.context
69 | """
70 | self.addOutput("RBFPropertySocket", strings.MODIFIER_LABEL)
71 |
72 | def draw(self, context, layout):
73 | """Draw the content of the node.
74 |
75 | :param context: The current context.
76 | :type context: bpy.context
77 | :param layout: The current layout.
78 | :type layout: bpy.types.UILayout
79 | """
80 | common.drawModifierProperties(self, layout)
81 |
82 | def draw_buttons_ext(self, context, layout):
83 | """Draw node buttons in the sidebar.
84 |
85 | :param context: The current context.
86 | :type context: bpy.context
87 | :param layout: The current layout.
88 | :type layout: bpy.types.UILayout
89 | """
90 | self.draw(context, layout)
91 |
92 | # ------------------------------------------------------------------
93 | # Getter
94 | # ------------------------------------------------------------------
95 |
96 | def getProperties(self, obj):
97 | """Return the name of the selected custom property.
98 |
99 | :param obj: The object to query.
100 | :type obj: bpy.types.Object
101 |
102 | :return: A list with the selected custom property and the value
103 | as a tuple.
104 | :rtype: list(tuple(str, float))
105 | """
106 | return common.getModifierProperties(self, obj)
107 |
--------------------------------------------------------------------------------
/RBFNodes/editor/nodes/node.py:
--------------------------------------------------------------------------------
1 | #
2 |
3 | import bpy
4 |
5 | from ... import var
6 |
7 |
8 | class RBFNode(bpy.types.Node):
9 | """Class, which all driver tree nodes inherit from.
10 | """
11 | bl_width_default = var.NODE_WIDTH
12 |
13 | editable : bpy.props.BoolProperty(default=True)
14 |
15 | @classmethod
16 | def poll(cls, nodeTree):
17 | """Make the node only visible to the RBF node tree.
18 |
19 | :param nodeTree: The current node tree.
20 | :type nodeTree: bpy.types.NodeTree
21 |
22 | :return: True, if the node tree is related to the node.
23 | :rtype: bool
24 | """
25 | return nodeTree.bl_idname == var.NODE_TREE_TYPE
26 |
27 | def addInput(self, socketType, name, **kwargs):
28 | """Add a new input socket to the node.
29 |
30 | :param socketType: The type of socket to add.
31 | :type socketType: class
32 | :param name: The name of the socket.
33 | :type name: str
34 | :param kwargs: Optional keyword arguments
35 | :type kwargs: div
36 |
37 | :return: The created socket object.
38 | :rtype: bpy.types.NodeSocket
39 | """
40 | socket = self.inputs.new(socketType, name)
41 | for key, value in kwargs.items():
42 | setattr(socket, key, value)
43 | return socket
44 |
45 | def addOutput(self, socketType, name, **kwargs):
46 | """Add a new output socket to the node.
47 |
48 | :param socketType: The type of socket to add.
49 | :type socketType: class
50 | :param name: The name of the socket.
51 | :type name: str
52 | :param kwargs: Optional keyword arguments
53 | :type kwargs: div
54 |
55 | :return: The created socket object.
56 | :rtype: bpy.types.NodeSocket
57 | """
58 | socket = self.outputs.new(socketType, name)
59 | for key, value in kwargs.items():
60 | setattr(socket, key, value)
61 | return socket
62 |
63 | def draw_buttons(self, context, layout):
64 | """Draw node buttons.
65 |
66 | :param context: The current context.
67 | :type context: bpy.context
68 | :param layout: The current layout.
69 | :type layout: bpy.types.UILayout
70 | """
71 | self.draw(context, layout)
72 |
73 | def draw(self, context, layout):
74 | """Draw node buttons.
75 |
76 | :param context: The current context.
77 | :type context: bpy.context
78 | :param layout: The current layout.
79 | :type layout: bpy.types.UILayout
80 | """
81 | layout.use_property_decorate = False
82 |
--------------------------------------------------------------------------------
/RBFNodes/editor/nodes/node_input.py:
--------------------------------------------------------------------------------
1 | #
2 |
3 | import bpy
4 |
5 | from . import common, node
6 | from ... import language
7 |
8 |
9 | # Get the current language.
10 | strings = language.getLanguage()
11 |
12 |
13 | class RBFNodeInputNode(node.RBFNode):
14 | """Node tree input node.
15 | """
16 | bl_idname = "RBFNodeInputNode"
17 | bl_label = strings.NODE_INPUT_LABEL
18 | bl_icon = 'NODETREE'
19 |
20 | # ------------------------------------------------------------------
21 | # Properties
22 | # ------------------------------------------------------------------
23 |
24 | parentItems = [('MATERIAL', strings.MATERIAL_LABEL, ""),
25 | ('NODE_GROUP', strings.NODE_GROUP_LABEL, "")]
26 | nodeParent : bpy.props.EnumProperty(name="", items=parentItems)
27 | parentName : bpy.props.StringProperty(name=strings.PARENT_LABEL)
28 | nodeName : bpy.props.StringProperty(name=strings.NODE_LABEL)
29 | plugName : bpy.props.StringProperty(name=strings.PLUG_LABEL)
30 | propertyEnum : bpy.props.EnumProperty(name="",
31 | items=common.nodeItemsCallback,
32 | update=common.setPropertyPlugName)
33 |
34 | propertyPlugs = {}
35 |
36 | def init(self, context):
37 | """Initialize the node and add the sockets.
38 |
39 | :param context: The current context.
40 | :type context: bpy.context
41 | """
42 | self.addOutput("RBFNodeSocket", strings.NODE_LABEL)
43 |
44 | def draw(self, context, layout):
45 | """Draw the content of the node.
46 |
47 | :param context: The current context.
48 | :type context: bpy.context
49 | :param layout: The current layout.
50 | :type layout: bpy.types.UILayout
51 | """
52 | common.drawNodeProperties(self, layout)
53 |
54 | def draw_buttons_ext(self, context, layout):
55 | """Draw node buttons in the sidebar.
56 |
57 | :param context: The current context.
58 | :type context: bpy.context
59 | :param layout: The current layout.
60 | :type layout: bpy.types.UILayout
61 | """
62 | self.draw(context, layout)
63 |
64 | # ------------------------------------------------------------------
65 | # Getter
66 | # ------------------------------------------------------------------
67 |
68 | def getProperties(self):
69 | """Return the name of the selected custom property.
70 |
71 | :return: A list with the selected custom property and the value
72 | as a tuple.
73 | :rtype: list(tuple(str, float))
74 | """
75 | return common.getNodeProperties(self)
76 |
--------------------------------------------------------------------------------
/RBFNodes/editor/nodes/object_input.py:
--------------------------------------------------------------------------------
1 | #
2 |
3 | import bpy
4 |
5 | from . import common, node
6 | from ... import language
7 | from ... core import poses
8 |
9 |
10 | # Get the current language.
11 | strings = language.getLanguage()
12 |
13 |
14 | class RBFObjectInputNode(node.RBFNode):
15 | """Driver object input node.
16 | """
17 | bl_idname = "RBFObjectInputNode"
18 | bl_label = strings.OBJECT_INPUT_LABEL
19 | bl_icon = 'OBJECT_DATA'
20 |
21 | # ------------------------------------------------------------------
22 | # Properties
23 | # ------------------------------------------------------------------
24 |
25 | sceneObject : bpy.props.PointerProperty(type=bpy.types.Object)
26 | bone : bpy.props.StringProperty()
27 |
28 | def init(self, context):
29 | """Initialize the node and add the sockets.
30 |
31 | :param context: The current context.
32 | :type context: bpy.context
33 | """
34 | self.addOutput("RBFObjectSocket", strings.OBJECT_LABEL)
35 | self.addInput("RBFPropertySocket", strings.PROPERTIES_LABEL, link_limit=0)
36 |
37 | def draw(self, context, layout):
38 | """Draw the content of the node.
39 |
40 | :param context: The current context.
41 | :type context: bpy.context
42 | :param layout: The current layout.
43 | :type layout: bpy.types.UILayout
44 | """
45 | common.drawObjectProperties(self, context, layout)
46 |
47 | # ------------------------------------------------------------------
48 | # Getter
49 | # ------------------------------------------------------------------
50 |
51 | def getObject(self):
52 | """Return the selected object of the node.
53 |
54 | :return: The currently selected object.
55 | :rtype: bpy.types.Object
56 | """
57 | return common.getObjectFromNode(self)
58 |
--------------------------------------------------------------------------------
/RBFNodes/editor/nodes/object_output.py:
--------------------------------------------------------------------------------
1 | #
2 |
3 | import bpy
4 |
5 | from . import common, node
6 | from ... import language
7 |
8 |
9 | # Get the current language.
10 | strings = language.getLanguage()
11 |
12 |
13 | class RBFObjectOutputNode(node.RBFNode):
14 | """Driver object output node.
15 | """
16 | bl_idname = "RBFObjectOutputNode"
17 | bl_label = strings.OBJECT_OUTPUT_LABEL
18 | bl_icon = 'OBJECT_DATA'
19 |
20 | # ------------------------------------------------------------------
21 | # Properties
22 | # ------------------------------------------------------------------
23 |
24 | sceneObject : bpy.props.PointerProperty(type=bpy.types.Object)
25 | bone : bpy.props.StringProperty()
26 |
27 | def init(self, context):
28 | """Initialize the node and add the sockets.
29 |
30 | :param context: The current context.
31 | :type context: bpy.context
32 | """
33 | self.addOutput("RBFPropertySocket", strings.PROPERTIES_LABEL, link_limit=0)
34 | self.addInput("RBFObjectSocket", strings.OBJECT_LABEL)
35 |
36 | def draw(self, context, layout):
37 | """Draw the content of the node.
38 |
39 | :param context: The current context.
40 | :type context: bpy.context
41 | :param layout: The current layout.
42 | :type layout: bpy.types.UILayout
43 | """
44 | common.drawObjectProperties(self, context, layout)
45 |
46 | # ------------------------------------------------------------------
47 | # Getter
48 | # ------------------------------------------------------------------
49 |
50 | def getObject(self):
51 | """Return the selected object of the node.
52 |
53 | :return: The currently selected object.
54 | :rtype: bpy.types.Object
55 | """
56 | return common.getObjectFromNode(self)
57 |
--------------------------------------------------------------------------------
/RBFNodes/editor/nodes/pose.py:
--------------------------------------------------------------------------------
1 | #
2 |
3 | import bpy
4 |
5 | from . import node
6 | from ... import language
7 | from ... core import poses, rbf, utils
8 |
9 |
10 | # Get the current language.
11 | strings = language.getLanguage()
12 |
13 |
14 | class SkipCallback(object):
15 | """Class for preventing a button callback for the edit mode.
16 | """
17 | def __init__(self):
18 | self.state = False
19 |
20 |
21 | skip = SkipCallback()
22 |
23 |
24 | class RBFPoseNode(node.RBFNode):
25 | """Pose node.
26 | """
27 | bl_idname = "RBFPoseNode"
28 | bl_label = strings.POSE_LABEL
29 | bl_icon = 'ARMATURE_DATA'
30 |
31 | # ------------------------------------------------------------------
32 | # Property callbacks
33 | # ------------------------------------------------------------------
34 |
35 | def toggleEditPose(self, context):
36 | """Callback for toggling the edit pose checkbox.
37 | Enabling puts the current pose into edit mode, disabling stores
38 | the adjusted pose values.
39 |
40 | :param context: The current context.
41 | :type context: bpy.context
42 | """
43 | if self.edit_pose:
44 | if not poses.editMode.state:
45 | result = poses.editPose(context, self)
46 | if result:
47 | poses.editMode.state = True
48 | # If entering edit mode is not possible toggle the
49 | # checkbox state back.
50 | else:
51 | skip.state = True
52 | self.edit_pose = False
53 | # If another pose is already in edit mode toggle the
54 | # checkbox state back.
55 | else:
56 | skip.state = True
57 | self.edit_pose = False
58 | else:
59 | if not skip.state:
60 | rbf.update(context)
61 | poses.editMode.state = False
62 | skip.state = False
63 |
64 | # ------------------------------------------------------------------
65 | # Properties
66 | # ------------------------------------------------------------------
67 |
68 | edit_pose : bpy.props.BoolProperty(name="Edit",
69 | description=strings.ANN_EDIT_POSE,
70 | update=toggleEditPose)
71 |
72 | poseIndex : bpy.props.IntProperty()
73 | driverData : bpy.props.StringProperty()
74 | drivenData : bpy.props.StringProperty()
75 | driverSize : bpy.props.IntProperty()
76 | drivenSize : bpy.props.IntProperty()
77 |
78 | version : bpy.props.StringProperty()
79 |
80 | def init(self, context):
81 | """Initialize the node and add the sockets.
82 |
83 | :param context: The current context.
84 | :type context: bpy.context
85 | """
86 | self.addOutput("RBFPoseSocket", strings.POSE_LABEL)
87 | utils.setVersion(self)
88 |
89 | def draw(self, context, layout):
90 | """Draw the content of the node.
91 |
92 | :param context: The current context.
93 | :type context: bpy.context
94 | :param layout: The current layout.
95 | :type layout: bpy.types.UILayout
96 | """
97 | row = layout.row(align=True)
98 | row.prop(self, "edit_pose", toggle=True)
99 | row.separator(factor=1.0)
100 | row.operator("rbfnodes.recall_pose").nodeName = self.name
101 |
102 | def draw_buttons_ext(self, context, layout):
103 | """Draw node buttons in the sidebar.
104 |
105 | :param context: The current context.
106 | :type context: bpy.context
107 | :param layout: The current layout.
108 | :type layout: bpy.types.UILayout
109 | """
110 | self.draw(context, layout)
111 |
--------------------------------------------------------------------------------
/RBFNodes/editor/nodes/property_input.py:
--------------------------------------------------------------------------------
1 | #
2 |
3 | import bpy
4 |
5 | from . import common, node
6 | from ... import language
7 |
8 |
9 | # Get the current language.
10 | strings = language.getLanguage()
11 |
12 |
13 | class RBFPropertyInputNode(node.RBFNode):
14 | """Object property input node.
15 | """
16 | bl_idname = "RBFPropertyInputNode"
17 | bl_label = strings.PROPERTY_INPUT_LABEL
18 | bl_icon = 'PROPERTIES'
19 |
20 | # ------------------------------------------------------------------
21 | # Property callbacks
22 | # ------------------------------------------------------------------
23 |
24 | def setLabelCallback(self, context):
25 | """Callback for updating the node label based on the property
26 | selection.
27 |
28 | :param context: The current context.
29 | :type context: bpy.context
30 | """
31 | common.propertyLabelCallback(self)
32 |
33 | def propItems(self, context):
34 | """Callback for the property drop down menu to collect the names
35 | of all object properties of the connected object.
36 |
37 | :param context: The current context.
38 | :type context: bpy.context
39 |
40 | :return: A list with tuple items for the enum property.
41 | :rtype: list(tuple(str))
42 | """
43 | return common.propertyItemsCallback(self, source=False)
44 |
45 | # ------------------------------------------------------------------
46 | # Properties
47 | # ------------------------------------------------------------------
48 |
49 | propertyEnum : bpy.props.EnumProperty(name="", items=propItems, update=setLabelCallback)
50 |
51 | def init(self, context):
52 | """Initialize the node and add the sockets.
53 |
54 | :param context: The current context.
55 | :type context: bpy.context
56 | """
57 | self.addOutput("RBFPropertySocket", strings.PROPERTY_LABEL)
58 |
59 | def draw(self, context, layout):
60 | """Draw the content of the node.
61 |
62 | :param context: The current context.
63 | :type context: bpy.context
64 | :param layout: The current layout.
65 | :type layout: bpy.types.UILayout
66 | """
67 | common.drawPropertyProperties(self, layout)
68 |
69 | def draw_buttons_ext(self, context, layout):
70 | """Draw node buttons in the sidebar.
71 |
72 | :param context: The current context.
73 | :type context: bpy.context
74 | :param layout: The current layout.
75 | :type layout: bpy.types.UILayout
76 | """
77 | self.draw(context, layout)
78 |
79 | # ------------------------------------------------------------------
80 | # Getter
81 | # ------------------------------------------------------------------
82 |
83 | def getProperties(self, obj):
84 | """Return the name of the selected custom property.
85 |
86 | :param obj: The object to query.
87 | :type obj: bpy.types.Object
88 |
89 | :return: A list with the selected custom property and the value
90 | as a tuple.
91 | :rtype: list(tuple(str, float))
92 | """
93 | return common.getObjectProperties(self, obj)
94 |
--------------------------------------------------------------------------------
/RBFNodes/editor/nodes/rotation_input.py:
--------------------------------------------------------------------------------
1 | #
2 |
3 | import bpy
4 |
5 | from . import common, node
6 | from ... import language
7 |
8 |
9 | # Get the current language.
10 | strings = language.getLanguage()
11 |
12 |
13 | class RBFRotationInputNode(node.RBFNode):
14 | """Object rotation input node.
15 | """
16 | bl_idname = "RBFRotationInputNode"
17 | bl_label = strings.ROTATION_INPUT_LABEL
18 | bl_icon = 'ORIENTATION_GIMBAL'
19 |
20 | # ------------------------------------------------------------------
21 | # Properties
22 | # ------------------------------------------------------------------
23 |
24 | w_axis : bpy.props.BoolProperty(name=strings.W_LABEL, default=False)
25 | x_axis : bpy.props.BoolProperty(name=strings.X_LABEL, default=False)
26 | y_axis : bpy.props.BoolProperty(name=strings.Y_LABEL, default=False)
27 | z_axis : bpy.props.BoolProperty(name=strings.Z_LABEL, default=False)
28 |
29 | rotationMode : bpy.props.EnumProperty(name="", items=common.ROTATION_MODE, default='EULER')
30 |
31 | def init(self, context):
32 | """Initialize the node and add the sockets.
33 |
34 | :param context: The current context.
35 | :type context: bpy.context
36 | """
37 | self.addOutput("RBFPropertySocket", strings.ROTATION_LABEL)
38 |
39 | def draw(self, context, layout):
40 | """Draw the content of the node.
41 |
42 | :param context: The current context.
43 | :type context: bpy.context
44 | :param layout: The current layout.
45 | :type layout: bpy.types.UILayout
46 | """
47 | common.drawRotationProperties(self, layout)
48 |
49 | def draw_buttons_ext(self, context, layout):
50 | """Draw node buttons in the sidebar.
51 |
52 | :param context: The current context.
53 | :type context: bpy.context
54 | :param layout: The current layout.
55 | :type layout: bpy.types.UILayout
56 | """
57 | self.draw(context, layout)
58 |
59 | # ------------------------------------------------------------------
60 | # Getter
61 | # ------------------------------------------------------------------
62 |
63 | def getProperties(self, obj):
64 | """Return the selected rotation properties for the given object.
65 |
66 | :param obj: The object to query.
67 | :type obj: bpy.types.Object
68 |
69 | :return: A list with the selected rotation properties and their
70 | values as a tuple.
71 | :rtype: list(tuple(str, float))
72 | """
73 | return common.getRotationProperties(self, obj)
74 |
--------------------------------------------------------------------------------
/RBFNodes/editor/nodes/rotation_output.py:
--------------------------------------------------------------------------------
1 | #
2 |
3 | import bpy
4 |
5 | from . import common, node
6 | from ... import dev, language, preferences, var
7 | from ... core import driver
8 |
9 |
10 | # Get the current language.
11 | strings = language.getLanguage()
12 |
13 |
14 | class RBFRotationOutputNode(node.RBFNode):
15 | """Object rotation output node.
16 | """
17 | bl_idname = "RBFRotationOutputNode"
18 | bl_label = strings.ROTATION_OUTPUT_LABEL
19 | bl_icon = 'ORIENTATION_GIMBAL'
20 |
21 | # ------------------------------------------------------------------
22 | # Properties
23 | # ------------------------------------------------------------------
24 |
25 | def updateCallback(self, context):
26 | """Callback for any value changes.
27 |
28 | :param context: The current context.
29 | :type context: bpy.context
30 | """
31 | pass
32 |
33 | w_axis : bpy.props.BoolProperty(name=strings.W_LABEL, default=False)
34 | x_axis : bpy.props.BoolProperty(name=strings.X_LABEL, default=False)
35 | y_axis : bpy.props.BoolProperty(name=strings.Y_LABEL, default=False)
36 | z_axis : bpy.props.BoolProperty(name=strings.Z_LABEL, default=False)
37 |
38 | rotationMode : bpy.props.EnumProperty(name="", items=common.ROTATION_MODE, default='EULER')
39 |
40 | output : bpy.props.FloatVectorProperty(size=4, update=updateCallback)
41 | # The indices of the created drivers on the driven object.
42 | driverIndex : bpy.props.IntVectorProperty(size=4, default=(-1, -1, -1, -1))
43 | isDriver : bpy.props.BoolProperty(default=False)
44 |
45 | def init(self, context):
46 | """Initialize the node and add the sockets.
47 |
48 | :param context: The current context.
49 | :type context: bpy.context
50 | """
51 | self.addInput("RBFPropertySocket", strings.ROTATION_LABEL)
52 |
53 | def draw(self, context, layout):
54 | """Draw the content of the node.
55 |
56 | :param context: The current context.
57 | :type context: bpy.context
58 | :param layout: The current layout.
59 | :type layout: bpy.types.UILayout
60 | """
61 | common.drawRotationProperties(self, layout)
62 |
63 | if preferences.getPreferences().developerMode:
64 | col = layout.column(align=True)
65 | col.prop(self, "output")
66 |
67 | def draw_buttons_ext(self, context, layout):
68 | """Draw node buttons in the sidebar.
69 |
70 | :param context: The current context.
71 | :type context: bpy.context
72 | :param layout: The current layout.
73 | :type layout: bpy.types.UILayout
74 | """
75 | self.draw(context, layout)
76 |
77 | # ------------------------------------------------------------------
78 | # Getter
79 | # ------------------------------------------------------------------
80 |
81 | def getProperties(self, obj):
82 | """Return the selected rotation properties for the given object.
83 |
84 | :param obj: The object to query.
85 | :type obj: bpy.types.Object
86 |
87 | :return: A list with the selected rotation properties and their
88 | values as a tuple.
89 | :rtype: list(tuple(str, float))
90 | """
91 | return common.getRotationProperties(self, obj)
92 |
93 | def getOutputProperties(self):
94 | """Return the selected output properties.
95 |
96 | :return: A list with the node and the selected output property
97 | indices as a tuple.
98 | :rtype: list(bpy.types.Node, int)
99 | """
100 | result = []
101 |
102 | if self.rotationMode == 'EULER':
103 | if self.x_axis:
104 | result.append((self, 0))
105 | if self.y_axis:
106 | result.append((self, 1))
107 | if self.z_axis:
108 | result.append((self, 2))
109 | else:
110 | for i in range(4):
111 | result.append((self, i))
112 |
113 | return result
114 |
115 | # ------------------------------------------------------------------
116 | # Driver
117 | # ------------------------------------------------------------------
118 |
119 | def createDriver(self, nodeGroup, driven, rbfNode):
120 | """Create a driver for each selected axis of the driven object.
121 |
122 | :param nodeGroup: The node tree of the current RBF setup.
123 | :type nodeGroup: bpy.types.NodeGroup
124 | :param driven: The driven object.
125 | :type driven: bpy.types.Object
126 | :param rbfNode: The current RBF node.
127 | :type rbfNode: bpy.types.Node
128 | """
129 | # Clear the driver indices.
130 | self.driverIndex = [-1, -1, -1, -1]
131 | # Delete any existing driver.
132 | if rbfNode.active:
133 | self.deleteDriver(driven)
134 |
135 | rotMode = var.ROTATIONS[self.rotationMode]
136 |
137 | if self.rotationMode == 'EULER':
138 | axes = [(self.w_axis, 0), (self.x_axis, 1), (self.y_axis, 2), (self.z_axis, 3)]
139 | shiftIndex = 1
140 | else:
141 | axes = [(True, 0), (True, 1), (True, 2), (True, 3)]
142 | shiftIndex = 0
143 |
144 | for axis, index in axes:
145 | if (self.rotationMode != 'EULER' and index == 0) or index > 0:
146 | if axis:
147 | dataPath = 'nodes["{}"].output[{}]'.format(self.name, str(index-shiftIndex))
148 | driver.createNodeGroupDriver(nodeGroup, driven, dataPath, rotMode, index-shiftIndex)
149 | # Get the index of the created driver.
150 | self.driverIndex[index] = driver.getDriverIndex(driven, dataPath, rotMode, index-shiftIndex)
151 | self.isDriver = True
152 |
153 | def deleteDriver(self, obj):
154 | """Delete the driver for the given object.
155 |
156 | :param obj: The driven object.
157 | :type obj: bpy.types.Object
158 | """
159 | rotMode = var.ROTATIONS[self.rotationMode]
160 |
161 | if self.rotationMode == 'EULER':
162 | axes = [(self.w_axis, 0), (self.x_axis, 1), (self.y_axis, 2), (self.z_axis, 3)]
163 | shiftIndex = 1
164 | else:
165 | axes = [(True, 0), (True, 1), (True, 2), (True, 3)]
166 | shiftIndex = 0
167 |
168 | for axis, index in axes:
169 | if (self.rotationMode != 'EULER' and index == 0) or index > 0:
170 | if axis:
171 | result = obj.driver_remove(rotMode, index-shiftIndex)
172 | dev.log("Delete driver: {} {}[{}] : {}".format(obj, rotMode, index, result))
173 |
174 | # Clear the driver indices.
175 | self.driverIndex = [-1, -1, -1, -1]
176 |
177 | def enableDriver(self, obj, enable):
178 | """Enable or disable the driver FCurves for the given object.
179 |
180 | :param obj: The driven object.
181 | :type obj: bpy.types.Object
182 | :param enable: The enabled state of the driver FCurves.
183 | :type enable: bool
184 | """
185 | driver.enableDriver(self, obj, enable)
186 |
--------------------------------------------------------------------------------
/RBFNodes/editor/nodes/scale_input.py:
--------------------------------------------------------------------------------
1 | #
2 |
3 | import bpy
4 |
5 | from . import common, node
6 | from ... import language
7 |
8 |
9 | # Get the current language.
10 | strings = language.getLanguage()
11 |
12 |
13 | class RBFScaleInputNode(node.RBFNode):
14 | """Object scale input node.
15 | """
16 | bl_idname = "RBFScaleInputNode"
17 | bl_label = strings.SCALE_INPUT_LABEL
18 | bl_icon = 'FIXED_SIZE'
19 |
20 | # ------------------------------------------------------------------
21 | # Properties
22 | # ------------------------------------------------------------------
23 |
24 | x_axis : bpy.props.BoolProperty(name=strings.X_LABEL, default=False)
25 | y_axis : bpy.props.BoolProperty(name=strings.Y_LABEL, default=False)
26 | z_axis : bpy.props.BoolProperty(name=strings.Z_LABEL, default=False)
27 |
28 | def init(self, context):
29 | """Initialize the node and add the sockets.
30 |
31 | :param context: The current context.
32 | :type context: bpy.context
33 | """
34 | self.addOutput("RBFPropertySocket", strings.SCALE_LABEL)
35 |
36 | def draw(self, context, layout):
37 | """Draw the content of the node.
38 |
39 | :param context: The current context.
40 | :type context: bpy.context
41 | :param layout: The current layout.
42 | :type layout: bpy.types.UILayout
43 | """
44 | common.drawTransformProperties(self, layout)
45 |
46 | def draw_buttons_ext(self, context, layout):
47 | """Draw node buttons in the sidebar.
48 |
49 | :param context: The current context.
50 | :type context: bpy.context
51 | :param layout: The current layout.
52 | :type layout: bpy.types.UILayout
53 | """
54 | self.draw(context, layout)
55 |
56 | # ------------------------------------------------------------------
57 | # Getter
58 | # ------------------------------------------------------------------
59 |
60 | def getProperties(self, obj):
61 | """Return the selected scale properties for the given object.
62 |
63 | :param obj: The object to query.
64 | :type obj: bpy.types.Object
65 |
66 | :return: A list with the selected scale properties and their
67 | values as a tuple.
68 | :rtype: list(tuple(str, float))
69 | """
70 | return common.getScaleProperties(self, obj)
71 |
--------------------------------------------------------------------------------
/RBFNodes/editor/nodes/scale_output.py:
--------------------------------------------------------------------------------
1 | #
2 |
3 | import bpy
4 |
5 | from . import common, node
6 | from ... import language, preferences
7 | from ... core import driver
8 |
9 |
10 | # Get the current language.
11 | strings = language.getLanguage()
12 |
13 |
14 | class RBFScaleOutputNode(node.RBFNode):
15 | """Object scale output node.
16 | """
17 | bl_idname = "RBFScaleOutputNode"
18 | bl_label = strings.SCALE_OUTPUT_LABEL
19 | bl_icon = 'FIXED_SIZE'
20 |
21 | # ------------------------------------------------------------------
22 | # Properties
23 | # ------------------------------------------------------------------
24 |
25 | def updateCallback(self, context):
26 | """Callback for any value changes.
27 |
28 | :param context: The current context.
29 | :type context: bpy.context
30 | """
31 | pass
32 |
33 | x_axis : bpy.props.BoolProperty(name=strings.X_LABEL, default=False)
34 | y_axis : bpy.props.BoolProperty(name=strings.Y_LABEL, default=False)
35 | z_axis : bpy.props.BoolProperty(name=strings.Z_LABEL, default=False)
36 |
37 | output : bpy.props.FloatVectorProperty(update=updateCallback)
38 | # The indices of the created drivers on the driven object.
39 | driverIndex : bpy.props.IntVectorProperty(default=(-1, -1, -1))
40 | isDriver : bpy.props.BoolProperty(default=False)
41 |
42 | def init(self, context):
43 | """Initialize the node and add the sockets.
44 |
45 | :param context: The current context.
46 | :type context: bpy.context
47 | """
48 | self.addInput("RBFPropertySocket", strings.SCALE_LABEL)
49 |
50 | def draw(self, context, layout):
51 | """Draw the content of the node.
52 |
53 | :param context: The current context.
54 | :type context: bpy.context
55 | :param layout: The current layout.
56 | :type layout: bpy.types.UILayout
57 | """
58 | common.drawTransformProperties(self, layout)
59 |
60 | if preferences.getPreferences().developerMode:
61 | col = layout.column(align=True)
62 | col.prop(self, "output")
63 |
64 | def draw_buttons_ext(self, context, layout):
65 | """Draw node buttons in the sidebar.
66 |
67 | :param context: The current context.
68 | :type context: bpy.context
69 | :param layout: The current layout.
70 | :type layout: bpy.types.UILayout
71 | """
72 | self.draw(context, layout)
73 |
74 | # ------------------------------------------------------------------
75 | # Getter
76 | # ------------------------------------------------------------------
77 |
78 | def getProperties(self, obj):
79 | """Return the selected scale properties for the given object.
80 |
81 | :param obj: The object to query.
82 | :type obj: bpy.types.Object
83 |
84 | :return: A list with the selected scale properties and their
85 | values as a tuple.
86 | :rtype: list(tuple(str, float))
87 | """
88 | return common.getScaleProperties(self, obj)
89 |
90 | def getOutputProperties(self):
91 | """Return the selected output properties.
92 |
93 | :return: A list with the node and the selected output property
94 | indices as a tuple.
95 | :rtype: list(bpy.types.Node, int)
96 | """
97 | return common.getTransformOutputProperties(self)
98 |
99 | # ------------------------------------------------------------------
100 | # Driver
101 | # ------------------------------------------------------------------
102 |
103 | def createDriver(self, nodeGroup, driven, rbfNode):
104 | """Create a driver for each selected axis of the driven object.
105 |
106 | :param nodeGroup: The node tree of the current RBF setup.
107 | :type nodeGroup: bpy.types.NodeGroup
108 | :param driven: The driven object.
109 | :type driven: bpy.types.Object
110 | :param rbfNode: The current RBF node.
111 | :type rbfNode: bpy.types.Node
112 | """
113 | driver.createTransformDriver(self, nodeGroup, driven, "scale", rbfNode)
114 |
115 | def deleteDriver(self, obj):
116 | """Delete the driver for the given object.
117 |
118 | :param obj: The driven object.
119 | :type obj: bpy.types.Object
120 | """
121 | driver.deleteTransformDriver(self, obj, "scale")
122 |
123 | def enableDriver(self, obj, enable):
124 | """Enable or disable the driver FCurves for the given object.
125 |
126 | :param obj: The driven object.
127 | :type obj: bpy.types.Object
128 | :param enable: The enabled state of the driver FCurves.
129 | :type enable: bool
130 | """
131 | driver.enableDriver(self, obj, enable)
132 |
--------------------------------------------------------------------------------
/RBFNodes/editor/nodes/shapeKey_input.py:
--------------------------------------------------------------------------------
1 | #
2 |
3 | import bpy
4 |
5 | from . import common, node
6 | from ... import language
7 |
8 |
9 | # Get the current language.
10 | strings = language.getLanguage()
11 |
12 |
13 | class RBFShapeKeyInputNode(node.RBFNode):
14 | """Shape key input node.
15 | """
16 | bl_idname = "RBFShapeKeyInputNode"
17 | bl_label = strings.SHAPE_KEY_INPUT_LABEL
18 | bl_icon = 'SHAPEKEY_DATA'
19 |
20 | # ------------------------------------------------------------------
21 | # Property callbacks
22 | # ------------------------------------------------------------------
23 |
24 | def setLabelCallback(self, context):
25 | """Callback for updating the node label based on the shape key
26 | selection.
27 |
28 | :param context: The current context.
29 | :type context: bpy.context
30 | """
31 | common.shapeKeyLabelCallback(self)
32 |
33 | def propItems(self, context):
34 | """Callback for the property drop down menu to collect the names
35 | of all shape keys of the connected object.
36 |
37 | :param context: The current context.
38 | :type context: bpy.context
39 |
40 | :return: A list with tuple items for the enum property.
41 | :rtype: list(tuple(str))
42 | """
43 | return common.shapeKeyItemsCallback(self, source=False)
44 |
45 | # ------------------------------------------------------------------
46 | # Properties
47 | # ------------------------------------------------------------------
48 |
49 | modeItems = [('LIST', strings.AUTO_LABEL, ""),
50 | ('MANUAL', strings.MANUAL_LABEL, "")]
51 | mode : bpy.props.EnumProperty(items=modeItems)
52 | shapeName : bpy.props.StringProperty(name="", update=setLabelCallback)
53 | shapeNameEnum : bpy.props.EnumProperty(name="", items=propItems, update=setLabelCallback)
54 |
55 | def init(self, context):
56 | """Initialize the node and add the sockets.
57 |
58 | :param context: The current context.
59 | :type context: bpy.context
60 | """
61 | self.addOutput("RBFPropertySocket", strings.SHAPE_KEY_LABEL)
62 |
63 | def draw(self, context, layout):
64 | """Draw the content of the node.
65 |
66 | :param context: The current context.
67 | :type context: bpy.context
68 | :param layout: The current layout.
69 | :type layout: bpy.types.UILayout
70 | """
71 | common.drawShapeKeyProperties(self, layout)
72 |
73 | def draw_buttons_ext(self, context, layout):
74 | """Draw node buttons in the sidebar.
75 |
76 | :param context: The current context.
77 | :type context: bpy.context
78 | :param layout: The current layout.
79 | :type layout: bpy.types.UILayout
80 | """
81 | self.draw(context, layout)
82 |
83 | # ------------------------------------------------------------------
84 | # Getter
85 | # ------------------------------------------------------------------
86 |
87 | def getProperties(self, obj):
88 | """Return the name of the selected shape key.
89 |
90 | :param obj: The object to query.
91 | :type obj: bpy.types.Object
92 |
93 | :return: The selected shape key name and the value as a tuple.
94 | :rtype: tuple(str, float)
95 | """
96 | return common.getShapeKeyProperties(self, obj)
97 |
--------------------------------------------------------------------------------
/RBFNodes/editor/nodes/sockets/__init__.py:
--------------------------------------------------------------------------------
1 | #
2 |
3 | import bpy
4 |
5 | from . node_socket import RBFNodeSocket
6 | from . object_socket import RBFObjectSocket
7 | from . pose_socket import RBFPoseSocket
8 | from . property_socket import RBFPropertySocket
9 |
10 |
11 | # ----------------------------------------------------------------------
12 | # Registration
13 | # ----------------------------------------------------------------------
14 |
15 | # Collect all classes in a list for easier access.
16 | classes = [RBFNodeSocket,
17 | RBFObjectSocket,
18 | RBFPoseSocket,
19 | RBFPropertySocket]
20 |
21 |
22 | def register():
23 | """Register the socket classes.
24 | """
25 | for cls in classes:
26 | bpy.utils.register_class(cls)
27 |
28 |
29 | def unregister():
30 | """Unregister the socket classes.
31 | """
32 | for cls in classes:
33 | bpy.utils.unregister_class(cls)
34 |
--------------------------------------------------------------------------------
/RBFNodes/editor/nodes/sockets/node_socket.py:
--------------------------------------------------------------------------------
1 | #
2 |
3 | import bpy
4 |
5 | if bpy.app.version < (4, 0, 0):
6 | from . socket import RBFSocket_legacy3 as RBFSocket
7 | else:
8 | from . socket import RBFSocket
9 |
10 | from .... import language, var
11 |
12 |
13 | # Get the current language.
14 | strings = language.getLanguage()
15 |
16 |
17 | class RBFNodeSocket(bpy.types.NodeSocket, RBFSocket):
18 | """Object property socket"""
19 | bl_idname = "RBFNodeSocket"
20 | bl_label = strings.NODE_SOCKET_LABEL
21 |
22 | color = var.COLOR_GREEN_2
23 |
24 | def draw(self, context, layout, node, text):
25 | """Draw the content of the node.
26 |
27 | :param context: The current context.
28 | :type context: bpy.context
29 | :param layout: The current layout.
30 | :type layout: bpy.types.UILayout
31 | :param node: The node the socket belongs to.
32 | :type node: bpy.types.Node
33 | :param text: The text label to draw alongside properties.
34 | :type text: str
35 | """
36 | layout.label(text=text)
37 |
--------------------------------------------------------------------------------
/RBFNodes/editor/nodes/sockets/object_socket.py:
--------------------------------------------------------------------------------
1 | #
2 |
3 | import bpy
4 |
5 | if bpy.app.version < (4, 0, 0):
6 | from . socket import RBFSocket_legacy3 as RBFSocket
7 | else:
8 | from . socket import RBFSocket
9 |
10 | from .... import language, var
11 |
12 |
13 | # Get the current language.
14 | strings = language.getLanguage()
15 |
16 |
17 | class RBFObjectSocket(bpy.types.NodeSocket, RBFSocket):
18 | """Object data socket"""
19 | bl_idname = "RBFObjectSocket"
20 | bl_label = strings.OBJECT_SOCKET_LABEL
21 |
22 | color = var.COLOR_ORANGE
23 |
24 | def draw(self, context, layout, node, text):
25 | """Draw the content of the node.
26 |
27 | :param context: The current context.
28 | :type context: bpy.context
29 | :param layout: The current layout.
30 | :type layout: bpy.types.UILayout
31 | :param node: The node the socket belongs to.
32 | :type node: bpy.types.Node
33 | :param text: The text label to draw alongside properties.
34 | :type text: str
35 | """
36 | layout.label(text=text)
37 |
--------------------------------------------------------------------------------
/RBFNodes/editor/nodes/sockets/pose_socket.py:
--------------------------------------------------------------------------------
1 | #
2 |
3 | import bpy
4 |
5 | if bpy.app.version < (4, 0, 0):
6 | from . socket import RBFSocket_legacy3 as RBFSocket
7 | else:
8 | from . socket import RBFSocket
9 |
10 | from .... import language, var
11 |
12 |
13 | # Get the current language.
14 | strings = language.getLanguage()
15 |
16 |
17 | class RBFPoseSocket(bpy.types.NodeSocket, RBFSocket):
18 | """RBF pose socket"""
19 | bl_idname = "RBFPoseSocket"
20 | bl_label = strings.POSE_SOCKET_LABEL
21 |
22 | color = var.COLOR_CYAN
23 |
24 | def draw(self, context, layout, node, text):
25 | """Draw the content of the node.
26 |
27 | :param context: The current context.
28 | :type context: bpy.context
29 | :param layout: The current layout.
30 | :type layout: bpy.types.UILayout
31 | :param node: The node the socket belongs to.
32 | :type node: bpy.types.Node
33 | :param text: The text label to draw alongside properties.
34 | :type text: str
35 | """
36 | layout.label(text=text)
37 |
--------------------------------------------------------------------------------
/RBFNodes/editor/nodes/sockets/property_socket.py:
--------------------------------------------------------------------------------
1 | #
2 |
3 | import bpy
4 |
5 | if bpy.app.version < (4, 0, 0):
6 | from . socket import RBFSocket_legacy3 as RBFSocket
7 | else:
8 | from . socket import RBFSocket
9 |
10 | from .... import language, var
11 |
12 |
13 | # Get the current language.
14 | strings = language.getLanguage()
15 |
16 |
17 | class RBFPropertySocket(bpy.types.NodeSocket, RBFSocket):
18 | """Object property socket"""
19 | bl_idname = "RBFPropertySocket"
20 | bl_label = strings.PROPERTY_SOCKET_LABEL
21 |
22 | color = var.COLOR_GREY_2
23 |
24 | def draw(self, context, layout, node, text):
25 | """Draw the content of the node.
26 |
27 | :param context: The current context.
28 | :type context: bpy.context
29 | :param layout: The current layout.
30 | :type layout: bpy.types.UILayout
31 | :param node: The node the socket belongs to.
32 | :type node: bpy.types.Node
33 | :param text: The text label to draw alongside properties.
34 | :type text: str
35 | """
36 | layout.label(text=text)
37 |
--------------------------------------------------------------------------------
/RBFNodes/editor/nodes/sockets/socket.py:
--------------------------------------------------------------------------------
1 | #
2 |
3 | import bpy
4 |
5 | from .... import var
6 |
7 |
8 | class RBFSocket:
9 | """Basic socket class"""
10 | color : var.COLOR_GREY
11 | name : bpy.props.StringProperty()
12 | value : None
13 |
14 | @classmethod
15 | def draw_color_simple(cls):
16 | """Return the color of the socket.
17 |
18 | :param context: The current context.
19 | :type context: bpy.context
20 | :param node: The node the socket belongs to.
21 | :type node: bpy.types.Node
22 |
23 | :return: The socket color.
24 | :rtype: list(float, float, float, float)
25 | """
26 | return cls.color
27 |
28 |
29 | class RBFSocket_legacy3:
30 | """Basic socket class"""
31 | color : var.COLOR_GREY
32 | name : bpy.props.StringProperty()
33 | value : None
34 |
35 | def draw_color(self, context, node):
36 | """Return the color of the socket.
37 |
38 | :param context: The current context.
39 | :type context: bpy.context
40 | :param node: The node the socket belongs to.
41 | :type node: bpy.types.Node
42 |
43 | :return: The socket color.
44 | :rtype: list(float, float, float, float)
45 | """
46 | return self.color
47 |
--------------------------------------------------------------------------------
/RBFNodes/language.py:
--------------------------------------------------------------------------------
1 | #
2 |
3 | import bpy
4 |
5 | import importlib
6 |
7 |
8 | NAME = "RBFNodes"
9 | LANGUAGE = "ENGLISH"
10 | LANGUAGE_FILES = {"ENGLISH": "strings_en",
11 | "FRENCH": "strings_fr",
12 | "GERMAN": "strings_de",
13 | "JAPANESE": "strings_jp",
14 | "KOREAN": "strings_ko",
15 | "PORTUGUESE": "strings_pt",
16 | "CHINESE": "strings_zh",
17 | "SPANISH": "strings_es"}
18 | LANGUAGE_ITEMS = (("ENGLISH", "English (English)", ""),
19 | ("FRENCH", "French (Français)", ""),
20 | ("GERMAN", "German (Deutsch)", ""),
21 | ("JAPANESE", "Japanese (日本人)", ""),
22 | ("KOREAN", "Korean (한국어)", ""),
23 | ("PORTUGUESE", "Portuguese (Português)", ""),
24 | ("CHINESE", "Simplified Chinese (简体中文)", ""),
25 | ("SPANISH", "Spanish (Español)", ""))
26 |
27 |
28 | def getLanguage():
29 | """Return the language module.
30 |
31 | :return: The language module.
32 | :rtype: module
33 | """
34 | language = LANGUAGE
35 | if LANGUAGE not in LANGUAGE_FILES:
36 | language = "ENGLISH"
37 |
38 | modPath = "{}.locales.{}".format(NAME, LANGUAGE_FILES[language])
39 | return importlib.import_module(modPath)
40 |
41 |
42 | def reloadDependencies():
43 | """Reload the modules which are affected by a language change.
44 | """
45 | mods = ["preferences",
46 | "var",
47 | "ui.operators",
48 | "ui.panel",
49 | "nodes.categories",
50 | "nodes.nodeTree",
51 | "nodes.common",
52 | "nodes.custom_input",
53 | "nodes.custom_output",
54 | "nodes.location_input",
55 | "nodes.location_output",
56 | "nodes.modifier_input",
57 | "nodes.modifier_output",
58 | "nodes.node_input",
59 | "nodes.node_output",
60 | "nodes.object_input",
61 | "nodes.object_output",
62 | "nodes.pose",
63 | "nodes.property_input",
64 | "nodes.property_output",
65 | "nodes.rbf",
66 | "nodes.rotation_input",
67 | "nodes.rotation_output",
68 | "nodes.scale_input",
69 | "nodes.scale_output",
70 | "nodes.shapeKey_input",
71 | "nodes.shapeKey_output",
72 | "nodes.sockets.node_socket",
73 | "nodes.sockets.object_socket",
74 | "nodes.sockets.pose_socket",
75 | "nodes.sockets.property_socket",
76 | "core.handler",
77 | "core.matrix",
78 | "core.nodeTree",
79 | "core.poses",
80 | "core.rbf"]
81 | for mod in mods:
82 | moduleName = ".".join([NAME, mod])
83 | try:
84 | module = __import__(moduleName, fromlist=[""])
85 | importlib.reload(module)
86 |
87 | except (Exception, ):
88 | pass
89 |
--------------------------------------------------------------------------------
/RBFNodes/locales/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IngoClemens/blender/9826535aba0a162259c5ba54054fb62e2862ffe5/RBFNodes/locales/__init__.py
--------------------------------------------------------------------------------
/RBFNodes/locales/strings_jp.py:
--------------------------------------------------------------------------------
1 | #
2 |
3 |
4 | # ----------------------------------------------------------------------
5 | # Label
6 | # ----------------------------------------------------------------------
7 |
8 | # Common
9 | AXIS_ANGLE_LABEL = "軸角度"
10 | EULER_LABEL = "オイラー"
11 | QUATERNION_LABEL = "クォータニオン"
12 |
13 | # Preferences
14 | AUTO_LABEL_LABEL = "プロパティノードを自動ラベル設定"
15 | DEVELOPER_LABEL = "開発者モード"
16 | LANGUAGE_LABEL = "言語(再起動が必要)"
17 | LOG_DATA_LABEL = "RBFデータを記録"
18 | MAX_PROPERTIES_LABEL = "最大プロパティ"
19 |
20 | # Operators
21 | ACTIVATE_RBF_LABEL = "RBFをアクティブ化"
22 | ADD_POSE_LABEL = "ポーズを追加"
23 | CREATE_NODE_INPUT_LABEL = "入力ノードをリンク"
24 | CREATE_NODE_OUTPUT_LABEL = "出力ノードをリンク"
25 | CREATE_RBF_LABEL = "新しいRBFセットアップを作成"
26 | DUMP_POSE_LABEL = "ポーズをダンプ"
27 | DUMP_RBF_LABEL = "RBFをダンプ"
28 | EDIT_DRIVEN_LABEL = "ドリブンを編集"
29 | EDIT_DRIVER_LABEL = "ドライバを編集"
30 | RECALL_POSE_LABEL = "呼び出し"
31 | RESET_RBF_LABEL = "RBFをリセット"
32 |
33 | # Panel
34 | ACTIVATION_LABEL = "アクティベーション"
35 | CATEGORY_LABEL = "RBFノード"
36 | CREATE_LABEL = "作成"
37 | DEVELOPER_LABEL_SHORT = "開発者"
38 | EDIT_POSE_DATA_LABEL = "ポーズデータを編集"
39 | RBF_LABEL = "RBF"
40 | REPLACE_LABEL = "置換"
41 | SEARCH_LABEL = "検索"
42 |
43 | # Nodes
44 | AUTO_LABEL = "自動"
45 | CUSTOM_INPUT_LABEL = "カスタム入力"
46 | CUSTOM_OUTPUT_LABEL = "カスタム出力"
47 | CUSTOM_LABEL = "カスタム"
48 | DEVIATION_LABEL = "標準偏差"
49 | EDITOR_LABEL = "RBFノードエディタ"
50 | GAUSS_1_LABEL = "ガウス1"
51 | GAUSS_2_LABEL = "ガウス2"
52 | INPUT_LABEL = "入力"
53 | KERNEL_LABEL = "カーネル"
54 | LINEAR_LABEL = "線形"
55 | LOCATION_INPUT_LABEL = "位置入力"
56 | LOCATION_OUTPUT_LABEL = "位置出力"
57 | LOCATION_LABEL = "位置"
58 | MANUAL_LABEL = "手動"
59 | MATERIAL_LABEL = "マテリアル"
60 | MEAN_DISTANCE_LABEL = "平均距離"
61 | MODIFIER_INPUT_LABEL = "モディファイア入力"
62 | MODIFIER_OUTPUT_LABEL = "モディファイア出力"
63 | MODIFIER_LABEL = "モディファイア"
64 | MULTI_QUADRATIC_LABEL = "マルチクアドラティックバイハーモニック"
65 | MULTI_QUADRATIC_INVERSE_LABEL = "逆マルチクアドラティックバイハーモニック"
66 | NEGATIVE_WEIGHTS_LABEL = "負の重み"
67 | NODE_INPUT_LABEL = "ノード入力"
68 | NODE_GROUP_LABEL = "ノードグループ"
69 | NODE_OUTPUT_LABEL = "ノード出力"
70 | NODE_LABEL = "ノード"
71 | NODE_SOCKET_LABEL = "RBFノードソケット"
72 | NODES_LABEL = "ノード"
73 | OBJECT_INPUT_LABEL = "オブジェクト入力"
74 | OBJECT_OUTPUT_LABEL = "オブジェクト出力"
75 | OBJECT_LABEL = "オブジェクト"
76 | OBJECT_SOCKET_LABEL = "RBFオブジェクトソケット"
77 | OBJECTS_LABEL = "オブジェクト"
78 | OUTPUT_LABEL = "出力"
79 | PARENT_LABEL = "親"
80 | PLUG_LABEL = "プラグ"
81 | POSE_LABEL = "ポーズ"
82 | POSE_SOCKET_LABEL = "RBFポーズソケット"
83 | POSES_LABEL = "ポーズ"
84 | PROPERTIES_LABEL = "プロパティ"
85 | PROPERTY_INPUT_LABEL = "プロパティ入力"
86 | PROPERTY_LABEL = "プロパティ"
87 | PROPERTY_OUTPUT_LABEL = "プロパティ出力"
88 | PROPERTY_SOCKET_LABEL = "RBFプロパティソケット"
89 | RADIUS_LABEL = "半径"
90 | ROTATION_INPUT_LABEL = "回転入力"
91 | ROTATION_LABEL = "回転"
92 | ROTATION_OUTPUT_LABEL = "回転出力"
93 | SCALE_INPUT_LABEL = "スケール入力"
94 | SCALE_LABEL = "スケール"
95 | SCALE_OUTPUT_LABEL = "スケール出力"
96 | SELECT_LABEL = "––– 選択 –––"
97 | SHAPE_KEY_INPUT_LABEL = "シェイプキー入力"
98 | SHAPE_KEY_LABEL = "シェイプキー"
99 | SHAPE_KEY_OUTPUT_LABEL = "シェイプキー出力"
100 | THIN_PLATE_LABEL = "シンプレート"
101 | VARIANCE_LABEL = "分散"
102 | W_LABEL = "W"
103 | X_LABEL = "X"
104 | Y_LABEL = "Y"
105 | Z_LABEL = "Z"
106 |
107 |
108 | # ----------------------------------------------------------------------
109 | # Description/Annotation
110 | # ----------------------------------------------------------------------
111 |
112 | # Preferences
113 | ANN_AUTO_LABEL = "プロパティとシェイプキーノードのラベルをプロパティ名に基づいて設定"
114 | ANN_DEVELOPER = "開発者用の拡張機能を表示"
115 | ANN_LANGUAGE = "プロパティとメッセージの言語"
116 | ANN_LOG_DATA = "RBFデータをコマンド出力に書き込む"
117 | ANN_MAX_PROPERTIES = "ドライバーとして使用できるプロパティの数または駆動されるプロパティの数を定義します。"
118 |
119 | # Operators
120 | ANN_ACTIVATE_RBF = "RBFの初期化"
121 | ANN_ADD_POSE = "現在のRBFまたは選択したノードグラフに新しいポーズを作成"
122 | ANN_CREATE_NODE_INPUT = "現在のノードグラフの選択から新しい入力ノードを作成"
123 | ANN_CREATE_NODE_OUTPUT = "現在のノードグラフの選択から新しい出力ノードを作成"
124 | ANN_CREATE_RBF = "新しいRBFのデフォルトノードを持つノードツリーを作成"
125 | ANN_DUMP_POSE = "選択したポーズノードのポーズ値をコマンドラインに書き込む"
126 | ANN_DUMP_RBF = "ポーズの重み行列をコマンドラインに書き込む"
127 | ANN_EDIT_DRIVEN = "すべてのポーズのドリブンデータを検索および置換"
128 | ANN_EDIT_DRIVER = "すべてのポーズのドライバデータを検索および置換"
129 | ANN_RECALL_POSE = "RBFのプロパティを選択したポーズに合わせる"
130 | ANN_RESET_RBF = "RBFをデフォルトにリセットし、すべてのドライバを削除"
131 |
132 | # Panel
133 | INFO_RESET_TO_UPDATE = "更新するためにRBFをリセット"
134 | INFO_VERSION_MISMATCH = "バージョンの不一致"
135 |
136 | # Nodes
137 | ANN_EDIT_POSE = "選択したポーズを編集します。これにより、編集のためにRBFの対象にドライバが無効になります"
138 |
139 | # General
140 | INFO_ADDED_SHAPE_KEY = "既存のポーズに新しいシェイプキーを追加しました"
141 | INFO_UPDATE_1 = "現在のバージョンと互換性のないRBFノードセットアップです。"
142 | INFO_UPDATE_2 = "インストールされたバージョンは"
143 | INFO_UPDATE_3 = "次の手順でノードツリーを更新してください"
144 | INFO_UPDATE_4 = "1. RBFエディタのサイドパネルで 'RBFをリセット' を押します。"
145 | INFO_UPDATE_5 = "2. 同じパネルで 'RBFをアクティブ化' を押します。"
146 | INFO_REPLACE = "ポーズノード内の出現を置き換えました"
147 |
148 | # ----------------------------------------------------------------------
149 | # Warning/Error
150 | # ----------------------------------------------------------------------
151 |
152 | WARNING_DRIVER_MISMATCH = "ドライバの値の数が既存のポーズと異なります"
153 | WARNING_DRIVEN_MISMATCH = "ドリブンの値の数が既存のポーズと異なります"
154 | WARNING_INPUT_SIZE_EXCEEDED = "ポーズや入力値が多すぎます"
155 | WARNING_NO_DRIVEN = "定義されたドリブンオブジェクトまたはプロパティがありません"
156 | WARNING_NO_DRIVEN_OBJECT = "ドリブンオブジェクトが選択されていません"
157 | WARNING_NO_DRIVEN_PROPERTY = "ドリブンプロパティが定義されていません"
158 | WARNING_NO_DRIVER = "ドライバオブジェクトまたはプロパティが定義されていません"
159 | WARNING_NO_DRIVER_OBJECT = "ドライバオブジェクトが選択されていません"
160 | WARNING_NO_DRIVER_PROPERTY = "ドライバプロパティが定義されていません"
161 | WARNING_NO_POSE_NODES = "ノードツリー内でポーズノードが見つかりません"
162 | WARNING_NO_POSES = "ポーズが定義されていません"
163 | WARNING_NO_RBF_NODE = "ポーズを追加するRBFノードがありません"
164 | WARNING_NO_RBF_NODE_IN_TREE = "ノードツリー内でRBFノードが見つかりません"
165 | WARNING_NO_SEARCH_STRING = "検索対象の文字列がありません"
166 | WARNING_OUTPUT_SIZE_EXCEEDED = "ポーズまたは出力値が多すぎます"
167 | WARNING_POSE_MATRIX_SIZE_EXCEEDED = "最大ポーズ行列サイズを超えました"
168 | WARNING_SIMILAR_POSE = "1つのポーズにユニークな値がなく、別のポーズに似ています。ポーズインデックス"
169 | WARNING_POSES_MISMATCH = "入力と出力のポーズの数が異なります"
170 | WARNING_TITLE = "RBFノード警告"
171 | WARNING_WEIGHT_MATRIX_SIZE_EXCEEDED = "最大ウェイト行列サイズを超えました"
172 |
173 | ERROR_DECOMPOSITION = "分解エラー"
174 | ERROR_EVALUATION = "評価エラー"
175 | ERROR_PROPERTIES_MISMATCH = "ポーズおよびドリブンプロパティの数が一致しません。原因としてBlenderのバージョンの違いが考えられます"
176 | ERROR_WRONG_VALUE_TYPE = "格納された値がプロパティの値と一致しません。原因としてBlenderのバージョンの違いが考えられます"
177 |
--------------------------------------------------------------------------------
/RBFNodes/locales/strings_ko.py:
--------------------------------------------------------------------------------
1 | #
2 |
3 |
4 | # ----------------------------------------------------------------------
5 | # Label
6 | # ----------------------------------------------------------------------
7 |
8 | # Common
9 | AXIS_ANGLE_LABEL = "축 각도"
10 | EULER_LABEL = "오일러"
11 | QUATERNION_LABEL = "쿼터니온"
12 |
13 | # Preferences
14 | AUTO_LABEL_LABEL = "속성 노드 자동 레이블링"
15 | DEVELOPER_LABEL = "개발자 모드"
16 | LANGUAGE_LABEL = "언어 (재시작 필요)"
17 | LOG_DATA_LABEL = "RBF 데이터 로깅"
18 | MAX_PROPERTIES_LABEL = "최대 속성"
19 |
20 | # Operators
21 | ACTIVATE_RBF_LABEL = "RBF 활성화"
22 | ADD_POSE_LABEL = "포즈 추가"
23 | CREATE_NODE_INPUT_LABEL = "입력 노드 생성"
24 | CREATE_NODE_OUTPUT_LABEL = "출력 노드 생성"
25 | CREATE_RBF_LABEL = "새로운 RBF 구성 생성"
26 | DUMP_POSE_LABEL = "포즈 내보내기"
27 | DUMP_RBF_LABEL = "RBF 내보내기"
28 | EDIT_DRIVEN_LABEL = "제어 데이터 편집"
29 | EDIT_DRIVER_LABEL = "드라이버 편집"
30 | RECALL_POSE_LABEL = "리콜"
31 | RESET_RBF_LABEL = "RBF 재설정"
32 |
33 | # Panel
34 | ACTIVATION_LABEL = "활성화"
35 | CATEGORY_LABEL = "RBF 노드"
36 | CREATE_LABEL = "생성"
37 | DEVELOPER_LABEL_SHORT = "개발자"
38 | EDIT_POSE_DATA_LABEL = "포즈 데이터 편집"
39 | RBF_LABEL = "RBF"
40 | REPLACE_LABEL = "대체"
41 | SEARCH_LABEL = "검색"
42 |
43 | # Nodes
44 | AUTO_LABEL = "자동"
45 | CUSTOM_INPUT_LABEL = "사용자 정의 입력"
46 | CUSTOM_OUTPUT_LABEL = "사용자 정의 출력"
47 | CUSTOM_LABEL = "사용자 정의"
48 | DEVIATION_LABEL = "표준 편차"
49 | EDITOR_LABEL = "RBF 노드 편집기"
50 | GAUSS_1_LABEL = "가우시안 1"
51 | GAUSS_2_LABEL = "가우시안 2"
52 | INPUT_LABEL = "입력"
53 | KERNEL_LABEL = "커널"
54 | LINEAR_LABEL = "선형"
55 | LOCATION_INPUT_LABEL = "위치 입력"
56 | LOCATION_OUTPUT_LABEL = "위치 출력"
57 | LOCATION_LABEL = "위치"
58 | MANUAL_LABEL = "수동"
59 | MATERIAL_LABEL = "재료"
60 | MEAN_DISTANCE_LABEL = "평균 거리"
61 | MODIFIER_INPUT_LABEL = "수정자 입력"
62 | MODIFIER_OUTPUT_LABEL = "수정자 출력"
63 | MODIFIER_LABEL = "수정자"
64 | MULTI_QUADRATIC_LABEL = "다중 2차"
65 | MULTI_QUADRATIC_INVERSE_LABEL = "다중 2차 역함수"
66 | NEGATIVE_WEIGHTS_LABEL = "음수 가중치"
67 | NODE_INPUT_LABEL = "노드 입력"
68 | NODE_GROUP_LABEL = "노드 그룹"
69 | NODE_OUTPUT_LABEL = "노드 출력"
70 | NODE_LABEL = "노드"
71 | NODE_SOCKET_LABEL = "RBF 노드 소켓"
72 | NODES_LABEL = "노드"
73 | OBJECT_INPUT_LABEL = "객체 입력"
74 | OBJECT_OUTPUT_LABEL = "객체 출력"
75 | OBJECT_LABEL = "객체"
76 | OBJECT_SOCKET_LABEL = "RBF 객체 소켓"
77 | OBJECTS_LABEL = "객체"
78 | OUTPUT_LABEL = "출력"
79 | PARENT_LABEL = "상위"
80 | PLUG_LABEL = "소켓"
81 | POSE_LABEL = "포즈"
82 | POSE_SOCKET_LABEL = "RBF 포즈 소켓"
83 | POSES_LABEL = "포즈"
84 | PROPERTIES_LABEL = "속성"
85 | PROPERTY_INPUT_LABEL = "속성 입력"
86 | PROPERTY_LABEL = "속성"
87 | PROPERTY_OUTPUT_LABEL = "속성 출력"
88 | PROPERTY_SOCKET_LABEL = "RBF 속성 소켓"
89 | RADIUS_LABEL = "반지름"
90 | ROTATION_INPUT_LABEL = "회전 입력"
91 | ROTATION_LABEL = "회전"
92 | ROTATION_OUTPUT_LABEL = "회전 출력"
93 | SCALE_INPUT_LABEL = "크기 입력"
94 | SCALE_LABEL = "크기"
95 | SCALE_OUTPUT_LABEL = "크기 출력"
96 | SELECT_LABEL = "––– 선택 –––"
97 | SHAPE_KEY_INPUT_LABEL = "쉐이프 키 입력"
98 | SHAPE_KEY_LABEL = "쉐이프 키"
99 | SHAPE_KEY_OUTPUT_LABEL = "쉐이프 키 출력"
100 | THIN_PLATE_LABEL = "얇은 판"
101 | VARIANCE_LABEL = "분산"
102 | W_LABEL = "W"
103 | X_LABEL = "X"
104 | Y_LABEL = "Y"
105 | Z_LABEL = "Z"
106 |
107 |
108 | # ----------------------------------------------------------------------
109 | # Description/Annotation
110 | # ----------------------------------------------------------------------
111 |
112 | # Preferences
113 | ANN_AUTO_LABEL = "속성 및 쉐이프 키 노드에 속성 이름을 기반으로 레이블을 자동으로 설정합니다"
114 | ANN_DEVELOPER = "개발자 기능을 표시합니다"
115 | ANN_LANGUAGE = "속성 및 메시지에 대한 언어를 설정합니다"
116 | ANN_LOG_DATA = "RBF 데이터를 명령 출력에 기록합니다"
117 | ANN_MAX_PROPERTIES = "드라이버로 사용되거나 구동될 수 있는 속성의 수를 정의합니다."
118 |
119 | # Operators
120 | ANN_ACTIVATE_RBF = "RBF 초기화"
121 | ANN_ADD_POSE = "현재 RBF 또는 선택한 노드 그래프에 새로운 포즈를 만듭니다"
122 | ANN_CREATE_NODE_INPUT = "현재 노드 그래프 선택을 기반으로 새로운 입력 노드를 만듭니다"
123 | ANN_CREATE_NODE_OUTPUT = "현재 노드 그래프 선택을 기반으로 새로운 출력 노드를 만듭니다"
124 | ANN_CREATE_RBF = "새로운 RBF를 위한 기본 노드를 가진 새로운 노드 트리를 만듭니다"
125 | ANN_DUMP_POSE = "선택한 포즈 노드의 포즈 값을 명령 라인에 작성합니다"
126 | ANN_DUMP_RBF = "포즈의 가중치 행렬을 명령 라인에 작성합니다"
127 | ANN_EDIT_DRIVEN = "모든 포즈의 제어 데이터를 검색하고 대체합니다."
128 | ANN_EDIT_DRIVER = "모든 포즈의 드라이버 데이터를 검색하고 대체합니다."
129 | ANN_RECALL_POSE = "선택한 포즈와 일치하도록 RBF 속성을 설정합니다"
130 | ANN_RESET_RBF = "RBF를 기본값으로 재설정하고 모든 드라이버를 제거합니다"
131 |
132 | # Panel
133 | INFO_RESET_TO_UPDATE = "업데이트하려면 RBF를 재설정합니다"
134 | INFO_VERSION_MISMATCH = "버전 불일치"
135 |
136 | # Nodes
137 | ANN_EDIT_POSE = "선택한 포즈를 편집합니다. 이렇게 하면 편집을 위해 대상 RBF의 모든 드라이버가 비활성화됩니다."
138 |
139 | # General
140 | INFO_ADDED_SHAPE_KEY = "기존 포즈에 새로운 쉐이프 키가 추가되었습니다"
141 | INFO_UPDATE_1 = "RBF 노드 구성이 현재 버전과 호환되지 않습니다."
142 | INFO_UPDATE_2 = "설치된 버전은 다음과 같습니다"
143 | INFO_UPDATE_3 = "노드 트리를 다음 단계로 업데이트하십시오"
144 | INFO_UPDATE_4 = "1. RBF 편집기 사이드 바에서 'RBF 재설정'을 누릅니다."
145 | INFO_UPDATE_5 = "2. 같은 사이드 바에서 'RBF 활성화'를 누릅니다."
146 | INFO_REPLACE = "포즈 노드에서 발생을 대체합니다"
147 |
148 | # ----------------------------------------------------------------------
149 | # Warning/Error
150 | # ----------------------------------------------------------------------
151 |
152 | WARNING_DRIVER_MISMATCH = "드라이버 값 수가 기존 포즈와 다릅니다"
153 | WARNING_DRIVEN_MISMATCH = "제어 값 수가 기존 포즈와 다릅니다"
154 | WARNING_INPUT_SIZE_EXCEEDED = "포즈나 입력 값이 너무 많음"
155 | WARNING_NO_DRIVEN = "정의된 제어 대상 객체나 속성이 없음"
156 | WARNING_NO_DRIVEN_OBJECT = "선택된 제어 대상 객체가 없음"
157 | WARNING_NO_DRIVEN_PROPERTY = "정의된 제어 속성이 없음"
158 | WARNING_NO_DRIVER = "정의된 드라이버 객체나 속성이 없음"
159 | WARNING_NO_DRIVER_OBJECT = "선택된 드라이버 객체가 없음"
160 | WARNING_NO_DRIVER_PROPERTY = "정의된 드라이버 속성이 없음"
161 | WARNING_NO_POSE_NODES = "노드 트리에서 포즈 노드를 찾을 수 없음"
162 | WARNING_NO_POSES = "포즈가 정의되지 않음"
163 | WARNING_NO_RBF_NODE = "포즈를 추가할 RBF 노드가 없음"
164 | WARNING_NO_RBF_NODE_IN_TREE = "노드 트리에서 RBF 노드를 찾을 수 없음"
165 | WARNING_NO_SEARCH_STRING = "검색 문자열 없음"
166 | WARNING_OUTPUT_SIZE_EXCEEDED = "포즈나 출력 값이 너무 많음"
167 | WARNING_POSE_MATRIX_SIZE_EXCEEDED = "최대 포즈 매트릭스 크기 초과"
168 | WARNING_SIMILAR_POSE = "포즈에 고유한 값이 없고 다른 포즈와 유사함. 포즈 색인"
169 | WARNING_POSES_MISMATCH = "입력 및 출력 간 포즈 수가 다름"
170 | WARNING_TITLE = "RBF 노드 경고"
171 | WARNING_WEIGHT_MATRIX_SIZE_EXCEEDED = "최대 가중치 행렬 크기 초과"
172 |
173 | ERROR_DECOMPOSITION = "분해 오류"
174 | ERROR_EVALUATION = "평가 오류"
175 | ERROR_PROPERTIES_MISMATCH = "포즈 및 제어 속성 수가 일치하지 않음. 가능한 원인은 Blender 버전의 차이"
176 | ERROR_WRONG_VALUE_TYPE = "저장된 값이 속성 값과 일치하지 않음. 가능한 원인은 Blender 버전의 차이"
177 |
--------------------------------------------------------------------------------
/RBFNodes/locales/strings_zh.py:
--------------------------------------------------------------------------------
1 | #
2 |
3 |
4 | # ----------------------------------------------------------------------
5 | # Label
6 | # ----------------------------------------------------------------------
7 |
8 | # Common
9 | AXIS_ANGLE_LABEL = "轴角度"
10 | EULER_LABEL = "欧拉角"
11 | QUATERNION_LABEL = "四元数"
12 |
13 | # Preferences
14 | AUTO_LABEL_LABEL = "自动标签化属性节点"
15 | DEVELOPER_LABEL = "开发者模式"
16 | LANGUAGE_LABEL = "语言(需要重新启动)"
17 | LOG_DATA_LABEL = "记录 RBF 数据"
18 | MAX_PROPERTIES_LABEL = "最大属性"
19 |
20 | # Operators
21 | ACTIVATE_RBF_LABEL = "激活 RBF"
22 | ADD_POSE_LABEL = "添加姿势"
23 | CREATE_NODE_INPUT_LABEL = "创建输入节点"
24 | CREATE_NODE_OUTPUT_LABEL = "创建输出节点"
25 | CREATE_RBF_LABEL = "创建新的 RBF 配置"
26 | DUMP_POSE_LABEL = "导出姿势"
27 | DUMP_RBF_LABEL = "导出 RBF"
28 | EDIT_DRIVEN_LABEL = "编辑受控数据"
29 | EDIT_DRIVER_LABEL = "编辑驱动程序"
30 | RECALL_POSE_LABEL = "调用"
31 | RESET_RBF_LABEL = "重置 RBF"
32 |
33 | # Panel
34 | ACTIVATION_LABEL = "激活"
35 | CATEGORY_LABEL = "RBF 节点"
36 | CREATE_LABEL = "创建"
37 | DEVELOPER_LABEL_SHORT = "开发者"
38 | EDIT_POSE_DATA_LABEL = "编辑姿势数据"
39 | RBF_LABEL = "RBF"
40 | REPLACE_LABEL = "替换"
41 | SEARCH_LABEL = "搜索"
42 |
43 | # Nodes
44 | AUTO_LABEL = "自动"
45 | CUSTOM_INPUT_LABEL = "自定义输入"
46 | CUSTOM_OUTPUT_LABEL = "自定义输出"
47 | CUSTOM_LABEL = "自定义"
48 | DEVIATION_LABEL = "标准差"
49 | EDITOR_LABEL = "RBF 节点编辑器"
50 | GAUSS_1_LABEL = "高斯 1"
51 | GAUSS_2_LABEL = "高斯 2"
52 | INPUT_LABEL = "输入"
53 | KERNEL_LABEL = "核函数"
54 | LINEAR_LABEL = "线性"
55 | LOCATION_INPUT_LABEL = "位置输入"
56 | LOCATION_OUTPUT_LABEL = "位置输出"
57 | LOCATION_LABEL = "位置"
58 | MANUAL_LABEL = "手动"
59 | MATERIAL_LABEL = "材质"
60 | MEAN_DISTANCE_LABEL = "平均距离"
61 | MODIFIER_INPUT_LABEL = "修改输入"
62 | MODIFIER_OUTPUT_LABEL = "修改输出"
63 | MODIFIER_LABEL = "修改器"
64 | MULTI_QUADRATIC_LABEL = "多重二次函数"
65 | MULTI_QUADRATIC_INVERSE_LABEL = "多重二次反函数"
66 | NEGATIVE_WEIGHTS_LABEL = "负权重"
67 | NODE_INPUT_LABEL = "节点输入"
68 | NODE_GROUP_LABEL = "节点组"
69 | NODE_OUTPUT_LABEL = "节点输出"
70 | NODE_LABEL = "节点"
71 | NODE_SOCKET_LABEL = "RBF 节点插座"
72 | NODES_LABEL = "节点"
73 | OBJECT_INPUT_LABEL = "物体输入"
74 | OBJECT_OUTPUT_LABEL = "物体输出"
75 | OBJECT_LABEL = "物体"
76 | OBJECT_SOCKET_LABEL = "RBF 物体插座"
77 | OBJECTS_LABEL = "物体"
78 | OUTPUT_LABEL = "输出"
79 | PARENT_LABEL = "父级"
80 | PLUG_LABEL = "连接器"
81 | POSE_LABEL = "姿势"
82 | POSE_SOCKET_LABEL = "RBF 姿势插座"
83 | POSES_LABEL = "姿势"
84 | PROPERTIES_LABEL = "属性"
85 | PROPERTY_INPUT_LABEL = "属性输入"
86 | PROPERTY_LABEL = "属性"
87 | PROPERTY_OUTPUT_LABEL = "属性输出"
88 | PROPERTY_SOCKET_LABEL = "RBF 属性插座"
89 | RADIUS_LABEL = "半径"
90 | ROTATION_INPUT_LABEL = "旋转输入"
91 | ROTATION_LABEL = "旋转"
92 | ROTATION_OUTPUT_LABEL = "旋转输出"
93 | SCALE_INPUT_LABEL = "缩放输入"
94 | SCALE_LABEL = "缩放"
95 | SCALE_OUTPUT_LABEL = "缩放输出"
96 | SELECT_LABEL = "––– 选择 –––"
97 | SHAPE_KEY_INPUT_LABEL = "形状键输入"
98 | SHAPE_KEY_LABEL = "形状键"
99 | SHAPE_KEY_OUTPUT_LABEL = "形状键输出"
100 | THIN_PLATE_LABEL = "薄板"
101 | VARIANCE_LABEL = "方差"
102 | W_LABEL = "W"
103 | X_LABEL = "X"
104 | Y_LABEL = "Y"
105 | Z_LABEL = "Z"
106 |
107 |
108 | # ----------------------------------------------------------------------
109 | # Description/Annotation
110 | # ----------------------------------------------------------------------
111 |
112 | # Preferences
113 | ANN_AUTO_LABEL = "根据属性名称自动设置属性和形状键节点的标签"
114 | ANN_DEVELOPER = "显示开发者功能"
115 | ANN_LANGUAGE = "属性和消息的语言"
116 | ANN_LOG_DATA = "将 RBF 数据记录到命令输出中"
117 | ANN_MAX_PROPERTIES = "定义可以用作驱动或被驱动的属性数量。"
118 |
119 | # Operators
120 | ANN_ACTIVATE_RBF = "初始化 RBF"
121 | ANN_ADD_POSE = "为当前的 RBF 或所选节点图创建新姿势"
122 | ANN_CREATE_NODE_INPUT = "根据当前节点图的选择创建新输入节点"
123 | ANN_CREATE_NODE_OUTPUT = "根据当前节点图的选择创建新输出节点"
124 | ANN_CREATE_RBF = "为新的 RBF 创建具有默认节点的新节点树"
125 | ANN_DUMP_POSE = "将所选姿势节点的姿势值写入命令行"
126 | ANN_DUMP_RBF = "将姿势的权重矩阵写入命令行"
127 | ANN_EDIT_DRIVEN = "查找并替换所有姿势的受控数据"
128 | ANN_EDIT_DRIVER = "查找并替换所有姿势的驱动程序数据"
129 | ANN_RECALL_POSE = "调整 RBF 的属性以匹配所选的姿势"
130 | ANN_RESET_RBF = "将 RBF 重置为默认值并删除所有驱动程序"
131 |
132 | # Panel
133 | INFO_RESET_TO_UPDATE = "重置 RBF 以进行更新"
134 | INFO_VERSION_MISMATCH = "版本不匹配"
135 |
136 | # Nodes
137 | ANN_EDIT_POSE = "编辑所选的姿势。这将禁用目标 RBF 上的任何驱动程序以进行编辑"
138 |
139 | # General
140 | INFO_ADDED_SHAPE_KEY = "向现有姿势添加新形状键"
141 | INFO_UPDATE_1 = "RBF 节点配置与当前版本不兼容。"
142 | INFO_UPDATE_2 = "已安装的版本是"
143 | INFO_UPDATE_3 = "请按照以下步骤更新节点树"
144 | INFO_UPDATE_4 = "1. 在 RBF 编辑器的侧边栏中,单击“重置 RBF”。"
145 | INFO_UPDATE_5 = "2. 在相同的侧边栏中,单击“激活 RBF”。"
146 | INFO_REPLACE = "在姿势节点中替换"
147 |
148 | # ----------------------------------------------------------------------
149 | # Warning/Error
150 | # ----------------------------------------------------------------------
151 |
152 | WARNING_DRIVER_MISMATCH = "驱动程序值的数量与现有姿势不匹配"
153 | WARNING_DRIVEN_MISMATCH = "受控值的数量与现有姿势不匹配"
154 | WARNING_INPUT_SIZE_EXCEEDED = "姿势或输入值太多"
155 | WARNING_NO_DRIVEN = "未定义受控对象或属性"
156 | WARNING_NO_DRIVEN_OBJECT = "未选择受控对象"
157 | WARNING_NO_DRIVEN_PROPERTY = "未定义受控属性"
158 | WARNING_NO_DRIVER = "未定义驱动对象或属性"
159 | WARNING_NO_DRIVER_OBJECT = "未选择驱动对象"
160 | WARNING_NO_DRIVER_PROPERTY = "未定义驱动属性"
161 | WARNING_NO_POSE_NODES = "节点树中未找到姿势节点"
162 | WARNING_NO_POSES = "未定义姿势"
163 | WARNING_NO_RBF_NODE = "没有 RBF 节点可添加姿势"
164 | WARNING_NO_RBF_NODE_IN_TREE = "节点树中未找到 RBF 节点"
165 | WARNING_NO_SEARCH_STRING = "没有搜索字符串"
166 | WARNING_OUTPUT_SIZE_EXCEEDED = "姿势或输出值太多"
167 | WARNING_POSE_MATRIX_SIZE_EXCEEDED = "超出最大姿势矩阵大小"
168 | WARNING_SIMILAR_POSE = "一个姿势没有唯一值,与另一个姿势相似。姿势索引"
169 | WARNING_POSES_MISMATCH = "输入和输出之间的姿势数量不同"
170 | WARNING_TITLE = "RBF 节点警告"
171 | WARNING_WEIGHT_MATRIX_SIZE_EXCEEDED = "超出最大权重矩阵大小"
172 |
173 | ERROR_DECOMPOSITION = "分解错误"
174 | ERROR_EVALUATION = "评估错误"
175 | ERROR_PROPERTIES_MISMATCH = "姿势和受控属性的数量不匹配。可能的原因是 Blender 版本差异"
176 | ERROR_WRONG_VALUE_TYPE = "存储的值与属性值不匹配。可能的原因是 Blender 版本差异"
177 |
--------------------------------------------------------------------------------
/RBFNodes/preferences.py:
--------------------------------------------------------------------------------
1 | #
2 |
3 | import bpy
4 |
5 | from . import language, var
6 |
7 | import io
8 | import json
9 | import os
10 |
11 |
12 | LOCAL_PATH = os.path.dirname(__file__)
13 | CONFIG_PATH = os.path.join(LOCAL_PATH, var.CONFIG_NAME)
14 |
15 |
16 | # ----------------------------------------------------------------------
17 | # Configuration
18 | # ----------------------------------------------------------------------
19 |
20 | def readConfig():
21 | """Read the configuration file.
22 | If the file doesn't exist create and return the basic configuration.
23 |
24 | :return: The content of the configuration file.
25 | :rtype: dict
26 | """
27 | if os.path.exists(CONFIG_PATH):
28 | config = jsonRead(CONFIG_PATH)
29 | if config:
30 | return config
31 | return var.DEFAULT_CONFIG
32 | else:
33 | config = var.DEFAULT_CONFIG
34 | jsonWrite(CONFIG_PATH, config)
35 | return config
36 |
37 |
38 | def writeConfig(data):
39 | """Write the configuration file.
40 |
41 | :param data: The data to write.
42 | :type data: dict or list
43 | """
44 | jsonWrite(CONFIG_PATH, data)
45 |
46 |
47 | def jsonRead(filePath):
48 | """Return the content of the given json file. Return an empty
49 | dictionary if the file doesn't exist.
50 |
51 | :param filePath: The file path of the file to read.
52 | :type filePath: str
53 |
54 | :return: The content of the json file.
55 | :rtype: dict
56 | """
57 | if os.path.exists(filePath):
58 | try:
59 | with open(filePath, "r") as fileObj:
60 | return json.load(fileObj)
61 | except OSError as exception:
62 | print(exception)
63 |
64 |
65 | def jsonWrite(filePath, data):
66 | """Export the given data to the given json file.
67 |
68 | :param filePath: The file path of the file to write.
69 | :type filePath: str
70 | :param data: The data to write.
71 | :type data: dict or list
72 |
73 | :return: True, if writing was successful.
74 | :rtype: bool
75 | """
76 | try:
77 | with io.open(filePath, "w", encoding="utf8") as fileObj:
78 | writeString = json.dumps(data, sort_keys=True, indent=4, ensure_ascii=False)
79 | fileObj.write(str(writeString))
80 | return True
81 | except OSError as exception:
82 | print(exception)
83 | return False
84 |
85 |
86 | def updateConfiguration(self, context):
87 | """Property callback for updating the current configuration.
88 |
89 | :param context: The current context.
90 | :type context: bpy.context
91 | """
92 | props = {"autoLabel": "autoLabel",
93 | "developerMode": "developerMode",
94 | "language": "language",
95 | "logData": "logData",
96 | "propertyCount": "propertyCount"}
97 |
98 | prefs = getPreferences()
99 | config = {}
100 |
101 | for prop in props:
102 | data = getattr(prefs, prop)
103 | config[props[prop]] = data
104 |
105 | # Write the configuration.
106 | writeConfig(config)
107 |
108 |
109 | config = readConfig()
110 | language.LANGUAGE = config["language"]
111 |
112 |
113 | # ----------------------------------------------------------------------
114 | # Preferences
115 | # ----------------------------------------------------------------------
116 |
117 |
118 | # Get the current language.
119 | strings = language.getLanguage()
120 |
121 |
122 | def getPreferences():
123 | """Return the preferences of the add-on.
124 |
125 | :return: The add-on preferences.
126 | :rtype: bpy.types.AddonPreferences
127 | """
128 | prefs = bpy.context.preferences.addons[var.ID_NAME].preferences
129 | return prefs
130 |
131 |
132 | def updateLanguageCallback(self, context):
133 | """Property callback for changing the active language.
134 |
135 | :param context: The current context.
136 | :type context: bpy.context
137 | """
138 | updateConfiguration(self, context)
139 | # language.reloadDependencies()
140 | # Reload the add-on.
141 | # bpy.ops.script.reload()
142 |
143 |
144 | class RBFNODESPreferences(bpy.types.AddonPreferences):
145 | """Create the layout for the preferences.
146 | """
147 | bl_idname = var.ID_NAME
148 |
149 | # Get the current configuration.
150 | config = readConfig()
151 |
152 | autoLabel : bpy.props.BoolProperty(name=strings.AUTO_LABEL_LABEL,
153 | description=strings.ANN_AUTO_LABEL,
154 | default=var.AUTO_LABEL)
155 | developerMode : bpy.props.BoolProperty(name=strings.DEVELOPER_LABEL,
156 | description=strings.ANN_DEVELOPER,
157 | default=var.DEVELOPER_MODE)
158 | language: bpy.props.EnumProperty(name=strings.LANGUAGE_LABEL,
159 | items=language.LANGUAGE_ITEMS,
160 | description=strings.ANN_LANGUAGE,
161 | default=config["language"],
162 | update=updateLanguageCallback)
163 | logData : bpy.props.BoolProperty(name=strings.LOG_DATA_LABEL,
164 | description=strings.ANN_LOG_DATA,
165 | default=var.EXPOSE_DATA)
166 | propertyCount : bpy.props.IntProperty(name=strings.MAX_PROPERTIES_LABEL,
167 | description=strings.ANN_MAX_PROPERTIES,
168 | default=var.NUM_ARRAYS * var.MAX_LEN,
169 | min=1,
170 | update=updateLanguageCallback)
171 |
172 | def draw(self, context):
173 | """Draw the panel and it's properties.
174 |
175 | :param context: The current context.
176 | :type context: bpy.context
177 | """
178 | self.layout.use_property_split = True
179 |
180 | col = self.layout.column(align=True)
181 | col.prop(self, "language")
182 | col.separator()
183 | col.prop(self, "autoLabel")
184 | col.prop(self, "propertyCount")
185 | col.separator(factor=1.5)
186 | col.prop(self, "developerMode")
187 | if self.developerMode:
188 | col.prop(self, "logData")
189 |
190 |
191 | # ----------------------------------------------------------------------
192 | # Registration
193 | # ----------------------------------------------------------------------
194 |
195 | # Collect all classes in a list for easier access.
196 | classes = [RBFNODESPreferences]
197 |
198 |
199 | def register():
200 | """Register the add-on.
201 | """
202 | for cls in classes:
203 | bpy.utils.register_class(cls)
204 |
205 |
206 | def unregister():
207 | """Unregister the add-on.
208 | """
209 | for cls in classes:
210 | bpy.utils.unregister_class(cls)
211 |
--------------------------------------------------------------------------------
/RBFNodes/ui/__init__.py:
--------------------------------------------------------------------------------
1 | #
2 |
3 | import bpy
4 | import bpy.utils.previews
5 |
6 | from . import operators, panel
7 | # from . icons import toolIcons
8 |
9 |
10 | # ----------------------------------------------------------------------
11 | # Registration
12 | # ----------------------------------------------------------------------
13 |
14 | # Collect all classes in a list for easier access.
15 | classes = [operators.RBFNODES_OT_CreateRBF,
16 | operators.RBFNODES_OT_CreatePose,
17 | operators.RBFNODES_OT_CreateNodeInput,
18 | operators.RBFNODES_OT_CreateNodeOutput,
19 | operators.RBFNODES_OT_RecallPose,
20 | operators.RBFNODES_OT_ActivateRBF,
21 | operators.RBFNODES_OT_ResetRBF,
22 | operators.RBFNODES_OT_DumpPose,
23 | operators.RBFNODES_OT_DumpRBF,
24 | operators.RBFNODES_OT_SearchReplacePoseDriverData,
25 | operators.RBFNODES_OT_SearchReplacePoseDrivenData,
26 | panel.RBF_Nodes_Properties,
27 | panel.RBFNODES_PT_RBF]
28 |
29 |
30 | def register():
31 | """Register the panel and operators.
32 | """
33 | # toolIcons.create()
34 |
35 | for cls in classes:
36 | bpy.utils.register_class(cls)
37 |
38 | bpy.types.WindowManager.rbf_nodes = bpy.props.PointerProperty(type=panel.RBF_Nodes_Properties)
39 |
40 |
41 | def unregister():
42 | """Unregister the panel and operators.
43 | """
44 | # toolIcons.delete()
45 |
46 | for cls in classes:
47 | bpy.utils.unregister_class(cls)
48 |
49 | del bpy.types.WindowManager.rbf_nodes
50 |
--------------------------------------------------------------------------------
/RBFNodes/ui/icons.py:
--------------------------------------------------------------------------------
1 | #
2 |
3 | import bpy
4 | import bpy.utils.previews
5 |
6 | import os
7 |
8 |
9 | class Icons(object):
10 | """Class for handling the tool icons.
11 | """
12 | def __init__(self):
13 | # Main dictionary for storing the icons.
14 | self.preview_collections = {}
15 |
16 | def create(self):
17 | """Set up the preview collection for giving access to the icons.
18 | """
19 | basePath = os.path.dirname(os.path.dirname(__file__))
20 | iconsDir = os.path.join(basePath, "icons")
21 |
22 | pcoll = bpy.utils.previews.new()
23 | pcoll.load("tool_icon", os.path.join(iconsDir, "RBFNodes.png"), 'IMAGE', True)
24 | self.preview_collections["icons"] = pcoll
25 |
26 | def delete(self):
27 | """Remove the preview collection.
28 | """
29 | for pcoll in self.preview_collections.values():
30 | bpy.utils.previews.remove(pcoll)
31 | self.preview_collections.clear()
32 |
33 | def getIcon(self, name):
34 | """Return the icon with the given name.
35 |
36 | :param name: The name of the icon.
37 | :type name: str
38 |
39 | :return: The icon.
40 | :rtype: bpy.types.ImagePreview
41 | """
42 | return self.preview_collections["icons"][name]
43 |
44 |
45 | toolIcons = Icons()
46 |
--------------------------------------------------------------------------------
/RBFNodes/ui/panel.py:
--------------------------------------------------------------------------------
1 | #
2 |
3 | import bpy
4 |
5 | from .. import language, preferences, var
6 | from .. core import nodeTree, utils
7 |
8 | '''
9 | # Custom icons:
10 | from . icons import toolIcons
11 | icon_value=toolIcons.getIcon("tool_icon").icon_id
12 | '''
13 |
14 |
15 | # Get the current language.
16 | strings = language.getLanguage()
17 |
18 |
19 | # ----------------------------------------------------------------------
20 | # Panel
21 | # ----------------------------------------------------------------------
22 |
23 | class RBF_Nodes_Properties(bpy.types.PropertyGroup):
24 | """Property group class to make the properties globally available.
25 | """
26 | search_value : bpy.props.StringProperty(name=strings.SEARCH_LABEL)
27 | replace_value : bpy.props.StringProperty(name=strings.REPLACE_LABEL)
28 |
29 |
30 | class RBFNODES_PT_RBF(bpy.types.Panel):
31 |
32 | bl_space_type = 'NODE_EDITOR'
33 | bl_region_type = 'UI'
34 | bl_category = strings.CATEGORY_LABEL
35 | bl_label = strings.RBF_LABEL
36 |
37 | @classmethod
38 | def poll(cls, context):
39 | """Only draw the panel when the RBF editor is active.
40 |
41 | :param context: The current context.
42 | :type context: bpy.context
43 | """
44 | return context.space_data.tree_type == var.NODE_TREE_TYPE
45 |
46 | def draw(self, context):
47 | """Draw the panel.
48 |
49 | :param context: The current context.
50 | :type context: bpy.context
51 | """
52 | rbfNodesProps = context.window_manager.rbf_nodes
53 |
54 | node = nodeTree.getRBFNode(context)
55 | if node is None or utils.verifyVersion(node):
56 | box = self.layout.box()
57 | box.label(text=strings.CREATE_LABEL)
58 | col = box.column(align=True)
59 | col.operator("rbfnodes.create_rbf", icon='FILE_NEW')
60 | col.separator(factor=1.5)
61 | col.operator("rbfnodes.create_pose", icon='POSE_HLT')
62 | col.separator(factor=1.5)
63 | col.operator("rbfnodes.create_node_input", icon='IMPORT')
64 | col.operator("rbfnodes.create_node_output", icon='EXPORT')
65 |
66 | box = self.layout.box()
67 | box.label(text=strings.ACTIVATION_LABEL)
68 | col = box.column(align=True)
69 | col.operator("rbfnodes.activate_rbf", icon='QUIT')
70 | col.separator(factor=1.5)
71 | col.operator("rbfnodes.reset_rbf", icon='CANCEL')
72 |
73 | if preferences.getPreferences().developerMode:
74 | box = self.layout.box()
75 | box.label(text=strings.DEVELOPER_LABEL_SHORT)
76 | col = box.column(align=True)
77 | col.operator("rbfnodes.dump_pose")
78 | col.operator("rbfnodes.dump_rbf")
79 | # col.separator(factor=1.5)
80 | box = self.layout.box()
81 | box.label(text=strings.EDIT_POSE_DATA_LABEL)
82 | col = box.column(align=True)
83 | col.prop(rbfNodesProps, "search_value")
84 | col.prop(rbfNodesProps, "replace_value")
85 | col.operator("rbfnodes.search_replace_pose_driver_data")
86 | col.operator("rbfnodes.search_replace_pose_driven_data")
87 | else:
88 | box = self.layout.box()
89 | box.label(text=strings.ACTIVATION_LABEL)
90 | col = box.column(align=True)
91 | col.label(text=strings.INFO_VERSION_MISMATCH)
92 | col.label(text=strings.INFO_RESET_TO_UPDATE)
93 | col.separator(factor=1.5)
94 | col.operator("rbfnodes.reset_rbf", icon='CANCEL')
95 |
--------------------------------------------------------------------------------
/RBFNodes/var.py:
--------------------------------------------------------------------------------
1 | #
2 |
3 |
4 | # ----------------------------------------------------------------------
5 | # Global names
6 | # ----------------------------------------------------------------------
7 | NAME = "RBF Nodes"
8 | ID_NAME = "RBFNodes"
9 | NODE_TREE_TYPE = "RBFNodesNodeTree"
10 | CATEGORIES_ID = "RBFNODES_TREE_NODE_CATEGORIES"
11 | CONFIG_NAME = "config.json"
12 |
13 | # ----------------------------------------------------------------------
14 | # Preferences
15 | # ----------------------------------------------------------------------
16 | AUTO_LABEL = True
17 | DEVELOPER_MODE = False
18 | EXPOSE_DATA = False
19 |
20 | DEFAULT_CONFIG = {
21 | "autoLabel": True,
22 | "developerMode": False,
23 | "language": "ENGLISH",
24 | "logData": False
25 | }
26 |
27 | # ----------------------------------------------------------------------
28 | # Socket colors
29 | # ----------------------------------------------------------------------
30 | COLOR_DEFAULT = [0.608, 0.608, 0.608, 1.0]
31 | COLOR_BLUE = (0.308, 0.771, 0.973, 1.0)
32 | COLOR_GREEN = (0.699, 0.862, 0.3154, 1.0)
33 | COLOR_GREEN_2 = (0.548, 0.736, 0.18, 1.0)
34 | COLOR_GREY = (0.73, 0.73, 0.73, 1)
35 | COLOR_GREY_2 = (0.6, 0.6, 0.6, 1)
36 | COLOR_ORANGE = (0.927, 0.614, 0.362, 1.0)
37 | COLOR_PURPLE = (0.875, 0.587, 1.0, 1.0)
38 | COLOR_RED = (0.933, 0.384, 0.342, 1.0)
39 | COLOR_YELLOW = (0.976, 0.905, 0.426, 1.0)
40 | COLOR_CYAN = (0.404, 0.843, 0.935, 1.0)
41 |
42 | # ----------------------------------------------------------------------
43 | # Node positioning
44 | # ----------------------------------------------------------------------
45 | NODE_WIDTH = 155
46 |
47 | INPUT_OBJECT_OFFSET = (-300, 100)
48 | OUTPUT_OBJECT_OFFSET = (330, 100)
49 | FIRST_POSE_OFFSET = (-300, -300)
50 | POSE_OFFSET = (0, -95)
51 | FIRST_NODE_OFFSET = (-330, -50)
52 | NODE_OFFSET = (0, -60)
53 |
54 | # ----------------------------------------------------------------------
55 | # Properties
56 | # ----------------------------------------------------------------------
57 | ROTATIONS = {'EULER': "rotation_euler",
58 | 'QUATERNION': "rotation_quaternion",
59 | 'AXIS_ANGLE': "rotation_axis_angle"}
60 |
61 | ARRAY_SIZE = 4
62 |
63 | # ----------------------------------------------------------------------
64 | # Matrix sizes
65 | # ----------------------------------------------------------------------
66 | # The maximum number of values per vector array. This number is fixed
67 | # as defined by Blender.
68 | MAX_LEN = 32
69 | # The number of concatenating arrays for storing pose and weight
70 | # matrices.
71 | # This number needs to be edited when the lists of floatVectorProperties
72 | # are getting extended.
73 | NUM_ARRAYS = 32
74 | # The maximum number of values which can be stored by concatenating the
75 | # floatVectorProperties.
76 | # MAX_SIZE = MAX_LEN * NUM_ARRAYS
77 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Add-ons for Blender
2 |
3 | This is an add-on collection for Blender which will be extended over time as production calls for the creation of new tools.
4 |
5 | ***If you find the add-ons useful we would appreciate if you would consider supporting us for continued development via [Gumroad](https://braverabbit.gumroad.com)***
6 |
7 | ## PickWalk
8 | Navigate through a hierarchy of objects or bones of an armature or walk over vertices and points of meshes and curves in edit mode.
9 | It basically copies the default pickwalking behaviour of Maya with additional selection extension and opposite side switching.
10 |
11 | [Documentation](https://github.com/IngoClemens/blender/wiki/PickWalk)
12 |
13 | ## Place Reflection
14 | Perform a reflection-based placement of lights (or any other object) on a mesh by simply dragging the mouse over the mesh.
15 |
16 | [Documentation](https://github.com/IngoClemens/blender/wiki/Place-Reflection)
17 |
18 | ## Rapid SDK
19 | One-button tool to quickly set up driving relationships between one or more properties, aimed to simplify the steps needed to adjust relative ranges or even non-linear behavior.
20 |
21 | [Documentation](https://github.com/IngoClemens/blender/wiki/Rapid-SDK)
22 |
23 | ## RBF Nodes
24 | Node-based RBF solver for driving multiple properties with multiple driver values.
25 |
26 | [Documentation](https://github.com/IngoClemens/blender/wiki/RBF-Nodes)
27 |
28 | ## Smooth Weights
29 | Advanced tool for smoothing mesh weights by averaging neighbouring weights.
30 |
31 | [Documentation](https://github.com/IngoClemens/blender/wiki/Smooth-Weights)
32 |
33 | ## Thumb Mate
34 | Create customizable thumbnail previews for object assets in the asset browser.
35 |
36 | [Documentation](https://github.com/IngoClemens/blender/wiki/Thumb-Mate)
37 |
38 | ## Tool Shelf
39 | Save scripts as buttons and organize them in groups for easy access.
40 |
41 | [Documentation](https://github.com/IngoClemens/blender/wiki/Tool-Shelf)
42 |
--------------------------------------------------------------------------------
/placeReflection/icons/placeReflection.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IngoClemens/blender/9826535aba0a162259c5ba54054fb62e2862ffe5/placeReflection/icons/placeReflection.png
--------------------------------------------------------------------------------
/smoothWeights/__init__.py:
--------------------------------------------------------------------------------
1 | """
2 | smoothWeights
3 | Copyright (C) 2023, Ingo Clemens, brave rabbit, www.braverabbit.com
4 |
5 | GNU GENERAL PUBLIC LICENSE Version 3
6 |
7 | This program is free software: you can redistribute it and/or modify
8 | it under the terms of the GNU General Public License as published by
9 | the Free Software Foundation, either version 3 of the License, or
10 | (at your option) any later version.
11 |
12 | This program is distributed in the hope that it will be useful,
13 | but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 | GNU General Public License for more details.
16 |
17 | You should have received a copy of the GNU General Public License
18 | along with this program.
19 | If not, see .
20 | """
21 |
22 | from . import preferences, panel, pies, smoothWeights, symmetryMap
23 |
24 |
25 | bl_info = {"name": "Smooth Weights",
26 | "author": "Ingo Clemens, braverabbit.com",
27 | "version": (2, 5, 1),
28 | "blender": (3, 6, 0),
29 | "category": "Rigging",
30 | "location": "View3D > Object",
31 | "description": "Smooth mesh weights by averaging neighbouring weights",
32 | "warning": "",
33 | "doc_url": "https://github.com/IngoClemens/blender/wiki/Smooth-Weights",
34 | "tracker_url": ""}
35 |
36 |
37 | # ----------------------------------------------------------------------
38 | # Registration
39 | # ----------------------------------------------------------------------
40 |
41 | def register():
42 | """Register the add-on.
43 | """
44 | preferences.register()
45 | pies.register()
46 | smoothWeights.register()
47 | symmetryMap.register()
48 |
49 | preferences.updatePanelLocation()
50 |
51 |
52 | def unregister():
53 | """Unregister the add-on.
54 | """
55 | preferences.unregister()
56 | pies.unregister()
57 | smoothWeights.unregister()
58 | symmetryMap.unregister()
59 |
60 | panel.unregister()
61 |
62 |
63 | if __name__ == "__main__":
64 | register()
65 |
--------------------------------------------------------------------------------
/smoothWeights/changelog.txt:
--------------------------------------------------------------------------------
1 | 2.5.1 - 2024-10-01
2 | - Symmetrizing the mesh now also works in Edit mode.
3 | - Fixed an error when trying to flip the mesh in Eddit mode.
4 | - Fixed an error when trying to mirror weights on an undeformed
5 | mesh.
6 |
7 | 2.5.0 - 2023-10-17
8 | - Added compatibility with Blender 4.0.
9 | - Improved smoothing of non-deformer groups for strokes after
10 | finishing the previous stroke.
11 | - Added a notification for meshes which include modifiers
12 | affecting the vertex count.
13 | - Changed the default order of pie menu items.
14 | - Removed the mesh properties panel location option because of
15 | context restrictions. The obsolete option is reset to 'Tool'.
16 | Please adjust the preference setting accordingly to avoid a
17 | warning message because of invalid settings.
18 | - Fixed a memory issue related to switching the affected vertex
19 | group type.
20 | - Language file fixes.
21 |
22 | 2.4.0 - 2023-10-05
23 | - Added vertex group types to smooth either all, only deforming or
24 | only non-deforming vertex groups.
25 | - Added a preference setting to define which panel the add-on
26 | appears in.
27 | - Added a pie menu as alternative option to the existing keymaps.
28 | - Added a language selector with 8 languages.
29 | - Fixed that the tool doesn't work in Weight Paint mode.
30 |
31 | 2.3.0 - 2023-09-14
32 | - Detection of non-manifold geometry when creating the symmetry
33 | map.
34 | - Two new operators for symmetrizing and flipping the mesh.
35 | - Sibling stepping for analyzing the symmetry. This requires
36 | extras to be enabled in the add-on preferences.
37 | - Code restructure
38 | - Easier modification of keymap preferences through a separate
39 | keymap file.
40 |
41 | 2.2.0 - 2023-09-12
42 | - Overall speed improvements.
43 | - Added a normalize option to the Limit Groups operator.
44 | - Mirroring weights now better addresses weights for vertices on
45 | the line of symmetry.
46 | - Removed the property to enable/disable the use of maximum vertex
47 | groups. Instead, a max groups value of zero will ignore any
48 | limit.
49 |
50 | 2.1.0 - 2023-09-08
51 | - Added the Mirror Weights operator to the Smooth Weights panel.
52 | - Added icons for all tool buttons.
53 | - Fixed that mapping takes long if too many elements are selected.
54 | The selection is now limited to two edges maximum.
55 | - Fixed that mapping fails with simple objects if one of the faces
56 | has a zero index.
57 | - Fixed that Add Partial doesn't find center vertices if these are
58 | slightly on the negative side.
59 |
60 | 2.0.0 - 2023-09-07
61 | - Added symmetry mapping.
62 | - Added a symmetry option to the smooth operators to use the new
63 | symmetry map for mirrored smoothing.
64 | - Added a new operator to clamp the maximum number of vertex
65 | groups.
66 | - Added a new operator to mirror vertex weights based on the
67 | symmetry map.
68 |
69 | 1.2.0 - 2023-08-28
70 | - Added an oversampling property for smoothing in multiple
71 | iterations for a smoother result.
72 | - Added an additional color preference to control the color of the
73 | brush circle and the tool info separately.
74 | - Increased the drawing precision of the brush circle.
75 | - Fixed that the flood operator would not respect the current
76 | vertex selection.
77 |
78 | 1.1.0 - 2023-08-25
79 | - Disabling Use Selection also disables selection/deselection
80 | mode.
81 | - When exiting while in Blender's Weight Paint mode the mode is
82 | kept.
83 | - Fixed that undo is very slow with a large number of affected
84 | vertices.
85 | - The user defined color for unselected vertices is not used when
86 | entering the tool.
87 |
88 | 1.0.0 - 2023-08-24
89 | - Increased brush value precision.
90 | - Public release.
91 |
92 | 0.0.6 - 2023-08-23
93 | - Ignore islands is now enabled for volume mode.
94 | - Added a selection and deselection mode.
95 | - Added a keymap for flooding in paint mode.
96 | - The paint mode undo queue has now a configurable length with a
97 | preference setting.
98 | - The tool is now only accessible when in Object or Weight Paint
99 | mode.
100 |
101 | 0.0.5 - 2023-08-18
102 | - Added the ability to drag the current frame while in paint
103 | mode with Shift + RMB.
104 | - Added the corresponding keymap to the in-view settings.
105 | - Fixed, that the brush circle briefly appears at the last
106 | position when starting a new stroke.
107 | - Fixed the wrong display of the max groups label in the in-view
108 | settings.
109 |
110 | 0.0.4 - 2023-08-18
111 | - Improvements to the smoothing algorithm.
112 | - Added the option to ignore backfaces while painting.
113 | - Included the volume option in the floor operator.
114 | - Removed the oversampling property, which was needed for the old
115 | smoothing process.
116 | - Fixed some issues related to the selection switches.
117 | - Fixed the strength value which was not working as expected.
118 |
119 | 0.0.3 - 2023-08-16
120 | - The tool is now also available in Blender's Weight Paint mode.
121 | - The paint cursor now shows as a target cross, to match it with
122 | the regular paint tool.
123 | - Fixed an undo-paint-related issue when using the flood operator.
124 | - Fixed the labels in the redo panel of the floor operator.
125 |
126 | 0.0.2 - 2023-08-16
127 | - Added a new option to disable any selection-based restriction.
128 | - Added a single undo operation while in paint mode.
129 | - Fixed the issue that any transformation affects the
130 | functionality of the tool.
131 |
132 | 0.0.1 - 2023-08-15
133 |
--------------------------------------------------------------------------------
/smoothWeights/constants.py:
--------------------------------------------------------------------------------
1 | #
2 |
3 | from . import language
4 |
5 |
6 | # Get the current language.
7 | strings = language.getLanguage()
8 |
9 |
10 | NAME = "smoothWeights"
11 | CONFIG_NAME = "config.json"
12 |
13 | PANEL_CLASS = "SMOOTHWEIGHTS_PT_settings"
14 |
15 | # Properties
16 | MAP_PROPERTY_NAME = "br_symmetry_map"
17 | META_PROPERTY_NAME = "br_symmetry_map_meta"
18 | WALK_PROPERTY_NAME = "br_walk_sibling_index"
19 |
20 | # ----------------------------------------------------------------------
21 | # Defaults
22 | # ----------------------------------------------------------------------
23 |
24 | # Preferences
25 | PANEL_LOCATIONS = ["TOOLS", "TAB", "PROPERTIES"]
26 | PANEL_LOCATION_ITEMS = (("TOOLS", strings.PANEL_LOCATION_TOOL_LABEL, ""),
27 | ("TAB", strings.PANEL_LOCATION_TAB_LABEL, ""))
28 | PANEL_AREAS = {"TOOLS": {"bl_label": "",
29 | "bl_space_type": 'VIEW_3D',
30 | "bl_region_type": 'UI',
31 | "bl_category": "Tool"},
32 | "TAB": {"bl_label": "",
33 | "bl_space_type": 'VIEW_3D',
34 | "bl_region_type": 'UI',
35 | "bl_category": ""}}
36 | PIE_AREAS = ["pie_west", "pie_east", "pie_south", "pie_north",
37 | "pie_north_west", "pie_north_east", "pie_south_west", "pie_south_east"]
38 | PIE_ITEMS = ["IGNORE_BACKSIDE",
39 | "USE_ISLANDS",
40 | "NORMALIZE",
41 | "OVERSAMPLING",
42 | "SELECTION",
43 | "USE_SYMMETRY",
44 | "VERTEX_GROUPS",
45 | "VOLUME",
46 | "NONE"]
47 | PIE_ENUMS = (("IGNORE_BACKSIDE", strings.IGNORE_BACKSIDE_LABEL, strings.ANN_IGNORE_BACKSIDE),
48 | ("USE_ISLANDS", strings.USE_ISLANDS_LABEL, strings.ANN_USE_ISLANDS),
49 | ("NORMALIZE", strings.NORMALIZE_LABEL, strings.ANN_NORMALIZE),
50 | ("OVERSAMPLING", strings.OVERSAMPLING_LABEL, strings.ANN_OVERSAMPLING),
51 | ("SELECTION", strings.SELECTION_LABEL, strings.ANN_USE_SELECTION),
52 | ("USE_SYMMETRY", strings.USE_SYMMETRY_LABEL, strings.ANN_USE_SYMMETRY),
53 | ("VERTEX_GROUPS", strings.VERTEX_GROUPS_LABEL, strings.ANN_VERTEX_GROUPS),
54 | ("VOLUME", strings.VOLUME_LABEL, strings.ANN_VOLUME),
55 | ("NONE", strings.EMPTY_LABEL, strings.ANN_EMPTY))
56 |
57 | DEFAULT_CONFIG = {
58 | "brushColor": [0.263, 0.723, 0.0],
59 | "extras": False,
60 | "infoColor": [0.263, 0.723, 0.0],
61 | "language": "ENGLISH",
62 | "keepSelection": True,
63 | "keymap": {
64 | "affectSelected": "A",
65 | "blend": "J",
66 | "clearSelection": "C",
67 | "deselect": "E",
68 | "flood": "F",
69 | "ignoreBackside": "X",
70 | "ignoreLock": "L",
71 | "islands": "I",
72 | "maxGroups": "G",
73 | "normalize": "N",
74 | "oversampling": "O",
75 | "pieMenu": "Y",
76 | "radius": "B",
77 | "select": "W",
78 | "strength": "S",
79 | "useSelection": "Q",
80 | "useSymmetry": "T",
81 | "value_down": "MINUS",
82 | "value_up": "PLUS",
83 | "vertexGroups": "D",
84 | "volume": "V",
85 | "volumeRange": "R"
86 | },
87 | "panelLocation": 'TOOLS',
88 | "panelName": strings.MENU_SMOOTH_WEIGHTS,
89 | "pieMenu": True,
90 | "selectedColor": [0.03, 0.302, 1.0],
91 | "showInfo": True,
92 | "showTime": False,
93 | "smoothPie": [
94 | "VERTEX_GROUPS",
95 | "OVERSAMPLING",
96 | "VOLUME",
97 | "USE_SYMMETRY",
98 | "SELECTION",
99 | "USE_ISLANDS",
100 | "IGNORE_BACKSIDE",
101 | "NORMALIZE"
102 | ],
103 | "undoSteps": 20,
104 | "unselectedColor": [1.0, 1.0, 1.0]
105 | }
106 |
107 |
108 | # Common
109 | # The number of weight group assignments.
110 | MAX_GROUPS = 5
111 | # Normalization switch.
112 | NORMALIZE = True
113 | # The vertex groups to affect.
114 | VERTEX_GROUPS = (("ALL", strings.VERTEX_GROUPS_ALL_LABEL, strings.ANN_VERTEX_GROUPS_ALL),
115 | ("DEFORM", strings.VERTEX_GROUPS_DEFORM_LABEL, strings.ANN_VERTEX_GROUPS_DEFORM),
116 | ("OTHER", strings.VERTEX_GROUPS_OTHER_LABEL, strings.ANN_VERTEX_GROUPS_OTHER))
117 | VERTEX_GROUPS_ITEMS = ["ALL", "DEFORM", "OTHER"]
118 |
119 |
120 | # Smooth Weights
121 | # The name of the color attribute which stores the current selection.
122 | SELECT_COLOR_ATTRIBUTE = "smoothWeights_selection"
123 | # The number of points for the brush circle.
124 | CIRCLE_POINTS = 64
125 |
126 | # Switch for smoothing only selected vertices.
127 | AFFECT_SELECTED = True
128 | # Switch for blending open vertex group borders.
129 | BLEND = True
130 | # The curve options.
131 | CURVE_ITEMS = (("NONE", strings.CURVE_NONE_LABEL, ""),
132 | ("LINEAR", strings.CURVE_LINEAR_LABEL, ""),
133 | ("SMOOTH", strings.SMOOTH_LABEL, ""),
134 | ("NARROW", strings.CURVE_NARROW_LABEL, ""))
135 | # The deselection state.
136 | DESELECT = False
137 | # Switch for ignoring backside faces.
138 | IGNORE_BACKSIDE = True
139 | # Switch for ignoring any locked weights.
140 | IGNORE_LOCK = False
141 | # The oversampling value.
142 | OVERSAMPLING = 1
143 | # The default radius of the brush.
144 | RADIUS = 0.25
145 | # The selection state.
146 | SELECT = False
147 | # The default strength of the brush.
148 | STRENGTH = 1.0
149 | # Value for skipping evaluation steps.
150 | UNDERSAMPLING = 3
151 | # Switch for handling islands as a continuous mesh.
152 | USE_ISLANDS = False
153 | # Switch for using selected or unselected vertices.
154 | USE_SELECTION = False
155 | # Switch for using the symmetry map.
156 | USE_SYMMETRY = False
157 | # Switch for toggling between a volume-based and a surface-based brush.
158 | VOLUME = False
159 | # The radius multiplier for finding close vertices in volume mode.
160 | VOLUME_RANGE = 0.2
161 |
162 |
163 | # Symmetry Map
164 | AXIS = "X"
165 | AXIS_INDICES = {"X": 0, "Y": 1, "Z": 2}
166 | DIRECTION = "POSITIVE"
167 | DIRECTION_INDICES = {"POSITIVE": 1, "NEGATIVE": 0}
168 | TOLERANCE = 0.0
169 | VERBOSE = False
170 |
171 | SIDE_LABELS = {"left": "right",
172 | "lt": "rt",
173 | "lft": "rgt",
174 | "lf": "rg",
175 | "l": "r"}
176 |
--------------------------------------------------------------------------------
/smoothWeights/dev.py:
--------------------------------------------------------------------------------
1 | #
2 |
3 | import importlib
4 | import os
5 |
6 |
7 | def reload(name=""):
8 | """Reload all modules.
9 |
10 | :param name: The name of the submodule to reload only or empty to
11 | reload the entire package.
12 | :type name: str
13 | """
14 | path = os.path.dirname(__file__)
15 | base = os.path.dirname(path)
16 | if len(name):
17 | path = os.path.join(path, name)
18 |
19 | for dirPath, dirs, fileList in os.walk(path):
20 | # Get the folder name from the current path.
21 | dirName = os.path.basename(dirPath)
22 | if dirName not in ["__pycache__"]:
23 | # Build the module name from the current path and strip away
24 | # the base path.
25 | # Replace the path separators with periods.
26 | modName = dirPath.replace(base, "").lstrip(os.sep).replace(os.sep, ".")
27 | for fileItem in fileList:
28 | if fileItem.endswith(".py"):
29 | # Reload the package.
30 | if fileItem == "__init__.py":
31 | moduleName = modName
32 | # Reload the module.
33 | else:
34 | moduleName = ".".join([modName, fileItem[:-3]])
35 | try:
36 | module = __import__(moduleName, fromlist=[""])
37 | importlib.reload(module)
38 | print("Reloading module: {}".format(moduleName))
39 | except Exception as exception:
40 | print(str(exception))
41 |
--------------------------------------------------------------------------------
/smoothWeights/language.py:
--------------------------------------------------------------------------------
1 | #
2 |
3 | import bpy
4 |
5 | import importlib
6 |
7 |
8 | NAME = "smoothWeights"
9 | LANGUAGE = "ENGLISH"
10 | LANGUAGE_FILES = {"ENGLISH": "strings_en",
11 | "FRENCH": "strings_fr",
12 | "GERMAN": "strings_de",
13 | "JAPANESE": "strings_jp",
14 | "KOREAN": "strings_ko",
15 | "PORTUGUESE": "strings_pt",
16 | "CHINESE": "strings_zh",
17 | "SPANISH": "strings_es"}
18 | LANGUAGE_ITEMS = (("ENGLISH", "English (English)", ""),
19 | ("FRENCH", "French (Français)", ""),
20 | ("GERMAN", "German (Deutsch)", ""),
21 | ("JAPANESE", "Japanese (日本人)", ""),
22 | ("KOREAN", "Korean (한국어)", ""),
23 | ("PORTUGUESE", "Portuguese (Português)", ""),
24 | ("CHINESE", "Simplified Chinese (简体中文)", ""),
25 | ("SPANISH", "Spanish (Español)", ""))
26 |
27 |
28 | def getLanguage():
29 | """Return the language module.
30 |
31 | :return: The language module.
32 | :rtype: module
33 | """
34 | language = LANGUAGE
35 | if LANGUAGE not in LANGUAGE_FILES:
36 | language = "ENGLISH"
37 |
38 | modPath = "{}.locales.{}".format(NAME, LANGUAGE_FILES[language])
39 | return importlib.import_module(modPath)
40 |
41 |
42 | def reloadDependencies():
43 | """Reload the modules which are affected by a language change.
44 | """
45 | mods = ["constants", "panel", "pies", "preferences", "smoothWeights", "symmetryMap"]
46 | for mod in mods:
47 | moduleName = ".".join([NAME, mod])
48 | try:
49 | module = __import__(moduleName, fromlist=[""])
50 | importlib.reload(module)
51 |
52 | except (Exception, ):
53 | pass
54 |
--------------------------------------------------------------------------------
/smoothWeights/locales/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IngoClemens/blender/9826535aba0a162259c5ba54054fb62e2862ffe5/smoothWeights/locales/__init__.py
--------------------------------------------------------------------------------
/smoothWeights/panel.py:
--------------------------------------------------------------------------------
1 | #
2 |
3 | from . import constants as const
4 | from . import language
5 |
6 | import bpy
7 |
8 | import types
9 |
10 |
11 | # Get the current language.
12 | strings = language.getLanguage()
13 |
14 |
15 | CLASS_ITEMS = {}
16 |
17 |
18 | def hasWeights(context):
19 | """Return, if the current object is a mesh and has at least one
20 | vertex group.
21 |
22 | :param context: The current context.
23 | :type context: bpy.context
24 |
25 | :return: True, if the object is a mesh and has weights.
26 | :rtype: bool
27 | """
28 | obj = context.object
29 | if (obj is not None and
30 | obj.type == 'MESH' and
31 | len(obj.vertex_groups) > 1):
32 | return True
33 | return False
34 |
35 |
36 | def buildPanelClass(attributes):
37 | """Build the panel class.
38 |
39 | :param attributes: The class attributes as a dictionary.
40 | :type attributes: dict
41 |
42 | :return: The class instance.
43 | :rtype: class
44 | """
45 | baseClass = bpy.types.Panel
46 |
47 | @classmethod
48 | def poll(cls, context):
49 | return hasWeights(context)
50 |
51 | # Create the draw method dynamically
52 | def draw(self, context):
53 | hasOrderMap = const.MAP_PROPERTY_NAME in context.object.data
54 |
55 | sw = context.object.smooth_weights
56 |
57 | layout = self.layout
58 |
59 | layout.use_property_split = True
60 | layout.use_property_decorate = False
61 |
62 | if context.object.mode in ['OBJECT', 'WEIGHT_PAINT']:
63 | row = layout.row()
64 | row.label(text=strings.PANEL_BRUSH_LABEL)
65 |
66 | box = layout.box()
67 | col = box.column(align=True)
68 | col.prop(sw, "curve")
69 | col.prop(sw, "radius")
70 | col.prop(sw, "strength")
71 | col.separator()
72 | col.prop(sw, "volume")
73 | col.prop(sw, "volumeRange")
74 | col.separator()
75 | col.prop(sw, "useSelection")
76 | col.prop(sw, "affectSelected")
77 | col.prop(sw, "ignoreBackside")
78 | col.prop(sw, "islands")
79 | col.prop(sw, "ignoreLock")
80 | col.prop(sw, "normalize")
81 | col.prop(sw, "oversampling")
82 | col.separator()
83 | col.prop(sw, "maxGroups")
84 | col.prop(sw, "vertexGroups")
85 | if sw.vertexGroups == "OTHER":
86 | col.prop(sw, "blend")
87 | if hasOrderMap:
88 | col.separator()
89 | col.prop(sw, "useSymmetry")
90 | col.separator()
91 | buttonCol = col.column(align=True)
92 | buttonCol.scale_y = 1.5
93 | buttonCol.operator("smoothweights.paint",
94 | text=strings.PANEL_BRUSH_LABEL,
95 | icon='BRUSH_DATA')
96 | col.separator()
97 |
98 | row = layout.row()
99 | row.label(text=strings.PANEL_TOOLS_LABEL)
100 |
101 | box = layout.box()
102 | col = box.column(align=True)
103 | if context.object.mode in ['OBJECT', 'WEIGHT_PAINT']:
104 | col.operator("smoothweights.flood", icon='IMAGE')
105 | col.separator()
106 | col.operator("smoothweights.limit_groups", icon='GROUP_VERTEX')
107 | if hasOrderMap:
108 | col.separator()
109 | col.operator("symmetryMap.mirror_weights", icon='MOD_MIRROR')
110 |
111 | # Define the class attributes.
112 | classAttrs = {key: value for key, value in attributes.items()}
113 | classAttrs.update({"bl_options": {'DEFAULT_CLOSED'},
114 | "poll": poll,
115 | "draw": draw})
116 |
117 | # Create the class.
118 | panelClass = type(const.PANEL_CLASS, (baseClass,), classAttrs)
119 |
120 | return panelClass
121 |
122 |
123 | def register(area, name):
124 | """Register the tool panel.
125 |
126 | :param area: The area string the panel should appear in.
127 | :type area: str
128 | :param name: The name of the panel.
129 | :type name: str
130 | """
131 | # Discard the mesh data properties location for the smooth panel
132 | # which has been introduced in version 2.4 but doesn't work due to
133 | # context restrictions.
134 | # An invalid enum option results in the area being empty.
135 | if not len(area):
136 | area = "TOOLS"
137 |
138 | # Get the class attributes and set the name.
139 | attrs = const.PANEL_AREAS[area]
140 | attrs["bl_label"] = name
141 | if area == "TAB":
142 | attrs["bl_category"] = name
143 | # Create the panel class.
144 | panelClass = buildPanelClass(attrs)
145 | # Register the panel.
146 | bpy.utils.register_class(panelClass)
147 | # Store the class instance for unregistering.
148 | CLASS_ITEMS[const.PANEL_CLASS] = panelClass
149 |
150 |
151 | def unregister():
152 | """Register the tool panel.
153 | """
154 | for item in CLASS_ITEMS:
155 | try:
156 | bpy.utils.unregister_class(CLASS_ITEMS[item])
157 | except (NameError, RuntimeError):
158 | pass
159 |
--------------------------------------------------------------------------------
/smoothWeights/usage.txt:
--------------------------------------------------------------------------------
1 | Requirements:
2 | The mesh needs to have at least two vertex groups for the panel and menu
3 | item to show.
4 |
5 | 1. Select the mesh in object mode. There is no need to enter weight
6 | painting mode.
7 | 2. From the side panel in the 3D view choose the tool tab.
8 | It's possible to customize the location of the tool from the add-on
9 | preferences.
10 | 3. Open the folder "Smooth Weights"
11 | 4. Press "Brush".
12 |
13 | or
14 |
15 | From the 3D view main menu choose Object > Smooth Weights.
16 |
17 | or
18 |
19 | From the 3D view Weight Paint menu choose Weights > Smooth Weights.
20 |
21 | 5. All settings can either be adjusted from the side panel before
22 | entering the tool or via keymaps while the tool is active.
23 | 6. LMB paint over the mesh to smooth the weights.
24 | 7. Press Enter to finish the tool or Esc/RMB to cancel.
25 |
26 | The default keymaps are:
27 |
28 | B: Brush size
29 | Hold B while dragging the cursor left/right to adjust the size.
30 | Hold Shift for faster adjustment and Ctrl for slower.
31 | Make sure to hover over the mesh.
32 | S: Brush strength
33 | Hold S while dragging the cursor left/right to adjust the strength.
34 | Hold Shift for faster adjustment and Ctrl for slower.
35 | Make sure to hover over the mesh.
36 | 1/2/3/4: Brush Curve Falloff (None, Linear, Smooth, Narrow)
37 | Q: Use Selection
38 | A: Affect Selected
39 | X: Ignore Backside
40 | L: Ignore Lock
41 | I: Use Islands
42 | N: Normalize
43 | O: Oversampling
44 | Hold O while pressing + or - to increase or decrease the oversampling
45 | value.
46 | V: Volume (off for surface smoothing)
47 | R: Volume Range
48 | Hold R while dragging the cursor left/right to adjust the range.
49 | Hold Shift for faster adjustment and Ctrl for slower.
50 | Make sure to hover over the mesh.
51 | G: Groups
52 | Hold G while pressing + or - to increase or decrease the number of
53 | vertex groups. Zero ignores any group limit.
54 | D: Vertex Group Type
55 | Hold D while pressing + or - to define which vertex group type is
56 | affected: Deformation, Non-Deformation or All
57 | J: Blend Open Vertex Group Borders (available with Vertex Group Type 'Other')
58 | T: Use Symmetry
59 | F: Flood Smooth
60 | Shift + RMB: Frame Change
61 | Change the current frame by dragging the mouse left or right.
62 | W: Toggle Select mode.
63 | E: Toggle Deselect mode.
64 | C: Clear Selection
65 |
66 | Y: Pie menu
67 |
68 | All keymaps, except the frame change, are customizable from the add-on
69 | preferences.
70 |
71 | While the tool is active the last brush stroke can be undone by pressing
72 | the default key for undo, without any modifiers.
73 |
--------------------------------------------------------------------------------
/toolShelf/__init__.py:
--------------------------------------------------------------------------------
1 | """
2 | toolShelf
3 | Copyright (C) 2021-2023, Ingo Clemens, brave rabbit, www.braverabbit.com
4 |
5 | GNU GENERAL PUBLIC LICENSE Version 3
6 |
7 | This program is free software: you can redistribute it and/or modify
8 | it under the terms of the GNU General Public License as published by
9 | the Free Software Foundation, either version 3 of the License, or
10 | (at your option) any later version.
11 |
12 | This program is distributed in the hope that it will be useful,
13 | but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 | GNU General Public License for more details.
16 |
17 | You should have received a copy of the GNU General Public License
18 | along with this program.
19 | If not, see .
20 |
21 | ------------------------------------------------------------------------
22 |
23 | Description:
24 |
25 | Tool Shelf provides a panel to create any number of grouped buttons to
26 | execute custom code and scripts to optimize workflow.
27 | The commands for each button can be either typed in directly or by
28 | retrieving it from the text editor. This works with a complete text
29 | document or any selection.
30 | Groups and buttons can be edited afterwards, as well as having their
31 | order changed.
32 | Button commands can be extracted at any time by copying them to the text
33 | editor.
34 |
35 | ------------------------------------------------------------------------
36 | """
37 |
38 | from . import preferences, toolShelf
39 |
40 |
41 | bl_info = {"name": "Tool Shelf",
42 | "author": "Ingo Clemens, braverabbit.com",
43 | "version": (0, 15, 1),
44 | "blender": (2, 92, 0),
45 | "category": "Interface",
46 | "location": "View3D",
47 | "description": "Save scripts as buttons and organize them in groups for easy access",
48 | "warning": "",
49 | "doc_url": "https://www.braverabbit.com/toolShelf",
50 | "tracker_url": ""}
51 |
52 |
53 | # ----------------------------------------------------------------------
54 | # Registration
55 | # ----------------------------------------------------------------------
56 |
57 | def register():
58 | """Register the add-on.
59 | """
60 | preferences.register()
61 | toolShelf.register()
62 |
63 |
64 | def unregister():
65 | """Unregister the add-on.
66 | """
67 | preferences.unregister()
68 | toolShelf.unregister()
69 |
70 |
71 | if __name__ == "__main__":
72 | register()
73 |
--------------------------------------------------------------------------------
/toolShelf/changelog.txt:
--------------------------------------------------------------------------------
1 | 0.15.1 - 2023-11-06
2 | - Fixed that the panel doesn't update after adding or removing
3 | tools.
4 |
5 | 0.15.0 - 2023-10-17
6 | - Added compatibility with Blender 4.0.
7 | - Added the language setting to the configuration.
8 | - Fixed that the language selection isn't kept after restarting.
9 |
10 | 0.14.0 - 2023-10-05
11 | - Abbreviate only non-english operator and property names.
12 |
13 | 0.13.0 - 2023-10-04
14 | - Added compatibility for non-english characters for names and
15 | tooltips.
16 | - Added a language selector with 8 languages.
17 |
18 | 0.12.1 - 2023-06-09
19 | - Assigned the PointerProperty to the WindowManager.
20 |
21 | 0.12.0 - 2023-04-17
22 | - Included a new option to add a color property.
23 |
24 | 0.11.0 - 2023-03-24
25 | - Added the option to open a file browser to provide a file path
26 | for the operator via the BROWSER_GET_FILE placeholder.
27 | Because of a current Blender bug only files can be selected,
28 | not folders.
29 | - Fixed that expanded enum properties are not displayed anymore.
30 |
31 | 0.10.0 - 2022-11-25
32 | - Property placeholders for single properties can now also be
33 | PROP1, to be consistent with the formatting for multiple
34 | properties.
35 | - Added a basic spell check for string and boolean property
36 | types.
37 | - Fixed that entries ending with a colon caused the tool to fail.
38 | - Added Substance3DInBlender to the black list of add-ons.
39 |
40 | 0.9.0 - 2022-02-10
41 | - Added an option to set a group to be expanded by default.
42 | - Added undo capability to operator buttons.
43 | - Fixed that editing is not possible when New Group is active in
44 | Add Mode.
45 |
46 | 0.8.0 - 2021-06-01
47 | - Added tooltips for enum properties.
48 | - Fixed that enum items don't show their labels.
49 |
50 | 0.7.0 - 2021-05-31
51 | - Changed that drawing of labels for properties in a row doesn't
52 | depend on the total number of properties but just the number of
53 | properties in a single row.
54 | - Added the option to expand enum properties to appear as a line
55 | of buttons.
56 | - Added the option to use the tool command as a property callback
57 | as an alternative to having to press the tool button to execute
58 | the tool.
59 |
60 | 0.6.0 - 2021-05-27
61 | - Added the option to add enum properties.
62 | - Added the option to align multiple properties in a row rather
63 | than having them appear as a column.
64 |
65 | 0.5.0 - 2021-05-20
66 | - Added the option to use unicode characters instead of a label
67 | for a button.
68 | - Added the option to only show the button icon.
69 | - String properties can be defined by entering string in the value
70 | field.
71 |
72 | 0.4.0 - 2021-05-16
73 | - Added the option to add properties to a tool or tool set.
74 |
75 | 0.3.0 - 2021-05-09
76 | - Added the option to combine multiple buttons as a set.
77 | - Tool names can be repeated across all groups.
78 | - Import of groups and tools from another configuration file.
79 |
80 | 0.2.0 - 2021-04-29
81 | - Added an option to define a button as an add-on toggle.
82 |
83 | 0.1.0 - 2021-04-22
84 | - First public release
85 |
--------------------------------------------------------------------------------
/toolShelf/cheatSheet.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IngoClemens/blender/9826535aba0a162259c5ba54054fb62e2862ffe5/toolShelf/cheatSheet.png
--------------------------------------------------------------------------------
/toolShelf/config.py:
--------------------------------------------------------------------------------
1 | #
2 |
3 | from . import constants as const
4 |
5 | import bpy
6 | import addon_utils
7 |
8 | import io
9 | import json
10 | import logging
11 | import os
12 |
13 |
14 | logger = logging.getLogger(__name__)
15 |
16 |
17 | LOCAL_PATH = os.path.dirname(__file__)
18 | CONFIG_PATH = os.path.join(LOCAL_PATH, const.CONFIG_NAME)
19 | ICONS_PATH = os.path.join(LOCAL_PATH, "icons")
20 | SCRIPTS_PATH = os.path.join(LOCAL_PATH, "scripts")
21 | BACKUP_PATH = os.path.join(LOCAL_PATH, "backup")
22 | BACKUP_COUNT = 5
23 |
24 |
25 | def createDir(dirPath):
26 | """Create the given folder if it doesn't exist.
27 |
28 | :param dirPath: The path of the folder to create.
29 | :type dirPath: str
30 |
31 | :return: The path of the folder.
32 | :rtype: str
33 | """
34 | if not os.path.exists(dirPath):
35 | try:
36 | os.makedirs(dirPath)
37 | except OSError as exception:
38 | logger.error(exception)
39 | return dirPath
40 |
41 |
42 | def configBase():
43 | """Return the basic dictionary of the tool shelf configuration.
44 |
45 | :return: The basic configuration dictionary.
46 | :rtype: dict
47 | """
48 | return {
49 | "name": "Tool Shelf",
50 | "category": "Tool Shelf",
51 | "label": "Tool Shelf",
52 | "language": const.LANGUAGE,
53 | "region": "UI",
54 | "space": "VIEW_3D",
55 | "base": "view3d",
56 | "groups": []
57 | }
58 |
59 |
60 | def readConfig():
61 | """Read the configuration file. If the file doesn't exist create the
62 | basic configuration and return it.
63 | Also return the default paths for icons, scripts and backup.
64 |
65 | :return: The content of the configuration file.
66 | :rtype: dict
67 | """
68 | for dirName in [ICONS_PATH, SCRIPTS_PATH, BACKUP_PATH]:
69 | createDir(dirName)
70 |
71 | if os.path.exists(CONFIG_PATH):
72 | return jsonRead(CONFIG_PATH)
73 | else:
74 | config = configBase()
75 | jsonWrite(CONFIG_PATH, config)
76 | return config
77 |
78 |
79 | def jsonRead(filePath):
80 | """Return the content of the given json file. Return an empty
81 | dictionary if the file doesn't exist.
82 |
83 | :param filePath: The file path of the file to read.
84 | :type filePath: str
85 |
86 | :return: The content of the json file.
87 | :rtype: dict
88 | """
89 | content = {}
90 |
91 | if os.path.exists(filePath):
92 | try:
93 | with open(filePath, "r") as fileObj:
94 | return json.load(fileObj)
95 | except OSError as exception:
96 | logger.error(exception)
97 | return content
98 |
99 |
100 | def jsonWrite(filePath, data):
101 | """Export the given data to the given json file.
102 |
103 | :param filePath: The file path of the file to write.
104 | :type filePath: str
105 | :param data: The data to write.
106 | :type data: dict or list
107 |
108 | :return: True, if writing was successful.
109 | :rtype: bool
110 | """
111 | try:
112 | with io.open(filePath, "w", encoding="utf8") as fileObj:
113 | writeString = json.dumps(addVersions(data), sort_keys=True, indent=4, ensure_ascii=False)
114 | fileObj.write(str(writeString))
115 | return True
116 | except OSError as exception:
117 | logger.error(exception)
118 | return False
119 |
120 |
121 | def addVersions(data):
122 | """Add the current tool and blender versions to the configuration.
123 |
124 | :param data: The configuration data.
125 | :type data: dict
126 |
127 | :return: The configuration data.
128 | :rtype: dict
129 | """
130 | version = [mod.bl_info["version"] for mod in addon_utils.modules() if mod.__name__ == const.NAME]
131 | if len(version):
132 | data["version"] = ".".join(str(i) for i in version[0])
133 | data["blenderVersion"] = bpy.app.version_string
134 | return data
135 |
136 |
137 | def backupConfig(data):
138 | """Backup the given configuration.
139 |
140 | :param data: The configuration dictionary to back up.
141 | :type data: dict
142 | """
143 | backupFileName = "config_{}.json".format(nextBackupIndex())
144 | backupFilePath = os.path.join(BACKUP_PATH, backupFileName)
145 | jsonWrite(backupFilePath, data)
146 |
147 |
148 | def nextBackupIndex():
149 | """Return the next index for the configuration backup. If the folder
150 | doesn't exist, create it.
151 | Get the index of the last file and compare it to the number of
152 | backup files.
153 |
154 | :return: The next index for the backup configuration.
155 | :rtype: int
156 | """
157 | fileList = os.listdir(createDir(BACKUP_PATH))
158 | if not len(fileList) or not fileList[-1].endswith(".json"):
159 | return 1
160 |
161 | lastIndex = int(fileList[-1].split(".")[0].split("_")[-1])
162 | newIndex = (lastIndex+1) % BACKUP_COUNT
163 |
164 | return newIndex
165 |
166 |
167 | def updateConfig(data):
168 | """Update older configurations with mandatory settings.
169 |
170 | :param data: The configuration data.
171 | :type data: dict
172 |
173 | :return: The updated configuration data.
174 | :rtype: dict
175 | """
176 | update = False
177 |
178 | if not "language" in data:
179 | data["language"] = const.LANGUAGE
180 | update = True
181 |
182 | # Cancel if no groups have been defined.
183 | if "groups" not in data:
184 | return data
185 |
186 | config = dict(data)
187 |
188 | updateItems = [("iconOnly", False)]
189 |
190 | for i in range(len(config["groups"])):
191 | group = config["groups"][i]
192 | for j in range(len(group["commands"])):
193 | command = group["commands"][j]
194 | if "set" in command:
195 | for k in range(len(command["commands"])):
196 | setCommand = command["commands"][k]
197 | for item in updateItems:
198 | if item[0] not in setCommand:
199 | config["groups"][i]["commands"][j]["commands"][k][item[0]] = item[1]
200 | update = True
201 | # Copy the properties to the set commands.
202 | if "valueName" in command and "valueName" not in setCommand:
203 | config["groups"][i]["commands"][j]["commands"][k]["valueName"] = command["valueName"]
204 | config["groups"][i]["commands"][j]["commands"][k]["value"] = command["value"]
205 | update = True
206 |
207 | else:
208 | for item in updateItems:
209 | if item[0] not in command:
210 | config["groups"][i]["commands"][j][item[0]] = item[1]
211 | update = True
212 |
213 | if update:
214 | # Backup the current configuration.
215 | backupConfig(data)
216 | # Save the configuration.
217 | jsonWrite(CONFIG_PATH, config)
218 |
219 | return config
220 |
--------------------------------------------------------------------------------
/toolShelf/constants.py:
--------------------------------------------------------------------------------
1 | #
2 |
3 |
4 | NAME = "toolShelf"
5 | CONFIG_NAME = "config.json"
6 |
7 | COLOR_LABELS = ["rgb", "color", "colour"]
8 | LANGUAGE = "ENGLISH"
9 |
10 | # ----------------------------------------------------------------------
11 | # List of add-ons not suitable for a toggle button.
12 | # ----------------------------------------------------------------------
13 |
14 | ADD_ON_BLACKLIST = ["animation_animall", "blender_id", "blenderkit", "curve_assign_shapekey",
15 | "curve_tools", "greasepencil_tools", "io_export_paper_model", "io_mesh_atomic",
16 | "magic_uv", "materials_library_vx", "mesh_auto_mirror", "mesh_bsurfaces",
17 | "mesh_tools", "object_boolean_tools", "object_collection_manager",
18 | "space_view3d_3d_navigation", "space_view3d_pie_menus", "Substance3DInBlender"]
19 |
--------------------------------------------------------------------------------
/toolShelf/language.py:
--------------------------------------------------------------------------------
1 | #
2 |
3 | import bpy
4 |
5 | import importlib
6 |
7 |
8 | NAME = "toolShelf"
9 | LANGUAGE = "ENGLISH"
10 | LANGUAGE_FILES = {"ENGLISH": "strings_en",
11 | "FRENCH": "strings_fr",
12 | "GERMAN": "strings_de",
13 | "JAPANESE": "strings_jp",
14 | "KOREAN": "strings_ko",
15 | "PORTUGUESE": "strings_pt",
16 | "CHINESE": "strings_zh",
17 | "SPANISH": "strings_es"}
18 | LANGUAGE_ITEMS = (("ENGLISH", "English (English)", ""),
19 | ("FRENCH", "French (Français)", ""),
20 | ("GERMAN", "German (Deutsch)", ""),
21 | ("JAPANESE", "Japanese (日本人)", ""),
22 | ("KOREAN", "Korean (한국어)", ""),
23 | ("PORTUGUESE", "Portuguese (Português)", ""),
24 | ("CHINESE", "Simplified Chinese (简体中文)", ""),
25 | ("SPANISH", "Spanish (Español)", ""))
26 |
27 |
28 | def getLanguage():
29 | """Return the language module.
30 |
31 | :return: The language module.
32 | :rtype: module
33 | """
34 | language = LANGUAGE
35 | if LANGUAGE not in LANGUAGE_FILES:
36 | language = "ENGLISH"
37 |
38 | modPath = "{}.locales.{}".format(NAME, LANGUAGE_FILES[language])
39 | return importlib.import_module(modPath)
40 |
--------------------------------------------------------------------------------
/toolShelf/locales/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IngoClemens/blender/9826535aba0a162259c5ba54054fb62e2862ffe5/toolShelf/locales/__init__.py
--------------------------------------------------------------------------------
/toolShelf/locales/strings_de.py:
--------------------------------------------------------------------------------
1 | #
2 |
3 | # ----------------------------------------------------------------------
4 | # Label
5 | # ----------------------------------------------------------------------
6 |
7 | NAME_LABEL = "Tool Shelf"
8 |
9 | # Preferences
10 | LANGUAGE_LABEL = "Sprache (Erfordert Neustart)"
11 |
12 | ADD_GROUP_LABEL = "Gruppe hinzufügen"
13 | ADD_TOOL_LABEL = "Tool hinzufügen"
14 | ADDON_TOGGLE_LABEL = "Add-on Umschalten"
15 | ADDON_LABEL = "Add-on"
16 | AFTER_GROUP_LABEL = "Nach Gruppe"
17 | AFTER_TOOL_LABEL = "Nach Tool"
18 | APPLY_ORDER_LABEL = "Reihenfolge Ändern"
19 | AUTO_EXPAND_LABEL = "Automatisch Aufklappen"
20 | COMMAND_LABEL = "Befehl"
21 | COMMANDS_LABEL = "Befehle"
22 | DELETE_LABEL = "Löschen"
23 | EDIT_LABEL = "Bearbeiten"
24 | EDIT_SET_LABEL = "Set Bearbeiten"
25 | GROUP_LABEL = "Gruppe"
26 | ICON_LABEL = "Icon"
27 | ICONS_LABEL = "Icon"
28 | ICON_ONLY_LABEL = "Nur Icon"
29 | IMPORT_LABEL = "Importieren"
30 | LABELS_LABEL = "Beschriftungen"
31 | MOVE_ITEM_DOWN_LABEL = "Element nach unten verschieben"
32 | MOVE_ITEM_UP_LABEL = "Element nach oben verschieben"
33 | MODE_LABEL = "Modus"
34 | MOVE_DOWN_LABEL = "Nach unten"
35 | MOVE_UP_LABEL = "Nach oben"
36 | NEW_GROUP_LABEL = "Neue Gruppe"
37 | NEW_NAME_LABEL = "Name"
38 | NEW_SET_LABEL = "Neues Set"
39 | PROPERTY_LABEL = "Eigenschaft"
40 | PROPERTY_VALUE_LABEL = "Wert"
41 | PROPERTY_CALLBACK_LABEL = "Befehl als Callback"
42 | ROW_BUTTONS_LABEL = "Schaltflächen Reihe"
43 | SELECT_ITEM_LABEL = "––– Wählen –––"
44 | SET_NAME_LABEL = "Set-Name"
45 | SETUP_LABEL = "Einrichtung"
46 | TOOL_LABEL = "Tool"
47 | TOOLTIP_LABEL = "Quickinfo"
48 | VIEW_COMMAND_LABEL = "Befehl anzeigen"
49 |
50 | # ----------------------------------------------------------------------
51 | # Description/Annotation
52 | # ----------------------------------------------------------------------
53 |
54 | # Preferences
55 | ANN_LANGUAGE = "Die Sprache für Eigenschaften und Meldungen"
56 |
57 | ANN_ADDON = "Das Add-on zum Erstellen eines Umschaltknopfs"
58 | ANN_COMMAND = ("Die Befehlszeichenfolge für die Schaltfläche.\n"
59 | "bpy import wird für einfache Befehle automatisch hinzugefügt.\n"
60 | "Leer lassen, um den Inhalt des Texteditors zu nutzen")
61 | ANN_EXECUTE_MODE = "Den aktuellen Modus ausführen"
62 | ANN_EXPAND = "Die Gruppe standardmäßig aufklappen"
63 | ANN_IMPORT_FILE = "Die Konfigurationsdatei zum Importieren von Gruppen oder Tools"
64 | ANN_IMPORT_ITEM = "Die zu importierende Gruppe oder das Tool"
65 | ANN_GROUP = ("Die Gruppe, zu der das Tool hinzugefügt werden soll.\n"
66 | "Eine neue Gruppe wird nach der letzten oder nach dem ausgewählten Gruppenelement erstellt")
67 | ANN_ICON = ("Der Dateiname des Icons mit 32x32 Pixeln oder ein Standard-Blender-Symbolname "
68 | "in einfachen Anführungszeichen oder ein Unicode-Zeichen")
69 | ANN_ICON_ONLY = "Nur das Symbol anzeigen, anstelle der Schaltflächenbeschriftung"
70 | ANN_ITEM_ADD_NEW = "Eine neue Gruppe oder ein neues Tool hinzufügen"
71 | ANN_ITEM_DELETE_EXISTING = "Eine vorhandene Gruppe oder Schaltfläche löschen"
72 | ANN_ITEM_DISPLAY_COMMAND = "Einen Schaltflächenbefehl im Texteditor anzeigen"
73 | ANN_ITEM_IMPORT = "Eine Gruppe oder ein Tool aus einer Konfigurationsdatei importieren"
74 | ANN_ITEM_OVERWRITE = ("Den Befehl einer vorhandenen Schaltfläche überschreiben.\nMit Sternchen * "
75 | "Symbol werden bestehende Einstellungen behalten")
76 | ANN_ITEM_REORDER = "Gruppen und Schaltflächen neu anordnen"
77 | ANN_MODE = "Den Bearbeitungsmodus ändern"
78 | ANN_NAME = ("Der Name der Gruppe oder des Tools.\n"
79 | "Er muss in Kleinbuchstaben in allen Gruppen eindeutig sein")
80 | ANN_NEW_GROUP = "Eine neue Gruppe anstelle eines neuen Tools hinzufügen"
81 | ANN_NEW_SET = "Ein neues Toolset hinzufügen"
82 | ANN_PROPERTY = "Eine numerische, boolesche oder String-Eigenschaft zum Werkzeug hinzufügen"
83 | ANN_PROPERTY_NAME = "Der Name der Eigenschaft"
84 | ANN_PROPERTY_VALUE = ("Der Standardwert für die Eigenschaft. Um Mindest- und Höchstwerte festzulegen, "
85 | "sollten die Werte auf folgende Weise eingegeben werden: Wert, Wert, Wert")
86 | ANN_PROPERTY_CALLBACK = ("Der Befehls-Code wird als Update-Callback-Funktion für alle "
87 | "Eigenschaften verwendet")
88 | ANN_SELECT_GROUP = "Eine Gruppe auswählen"
89 | ANN_SET_COLUMNS = "Die Anzahl der Schaltflächen in einer Zeile"
90 | ANN_SET_NAME = "Der Name des Schaltflächensatzes"
91 | ANN_TOGGLE_ADDON = "Eine Schaltfläche erstellen, um ein Add-on zu aktivieren oder zu deaktivieren"
92 | ANN_TOOL = "Der Tool-Befehl zum Bearbeiten"
93 | ANN_TOOLTIP = "Die Quickinfo für die Schaltfläche"
94 |
95 | # ----------------------------------------------------------------------
96 | # Info
97 | # ----------------------------------------------------------------------
98 |
99 |
100 | # ----------------------------------------------------------------------
101 | # Warning/Error
102 | # ----------------------------------------------------------------------
103 |
104 | WARNING_ADDON_INCOMPATIBLE = "Das Add-on ist nicht kompatibel und muss in den Einstellungen manuell aktiviert/deaktiviert werden: "
105 | WARNING_ADDON_MISSING = "Ein Add-on mit diesem Namen existiert nicht: "
106 | WARNING_BRACKETS_INCOMPLETE = "Die Klammern für die Eigenschaften sind unvollständig"
107 | WARNING_GROUP_NAME_EXISTS = "Der Gruppenname existiert bereits"
108 | WARNING_IMAGE_MISSING = "Das Bild existiert nicht im angegebenen Pfad: "
109 | WARNING_LABEL_COMMAND_MISMATCH = "Die Anzahl der Tool-Beschriftungen und -befehle stimmt nicht überein"
110 | WARNING_NO_GROUP_SELECTED = "Keine Gruppe ausgewählt"
111 | WARNING_NO_NAME = "Kein Name definiert"
112 | WARNING_NO_TEXT_EDITOR = "Es ist kein Texteditor geöffnet, um den Tool-Befehl abzurufen"
113 | WARNING_NO_TEXT_EDITOR_TO_VIEW = "Es ist kein Texteditor geöffnet, um den Befehl anzuzeigen"
114 | WARNING_NO_TOOL_IN_GROUP = "In der ausgewählten Gruppe existiert kein Tool"
115 | WARNING_NO_TOOL_SELECTED = "Kein Tool ausgewählt"
116 | WARNING_PROPERTY_NAME_MISSING = "Ein Eigenschaftsname und/oder ein Standardwert fehlen"
117 | WARNING_PROPERTY_VALUE_MISMATCH = "Die Anzahl der Eigenschaften und Werte stimmt nicht überein"
118 | WARNING_SELECT_CONFIG = "Eine gültige Konfigurationsdatei muss ausgewählt sein"
119 | WARNING_SELECT_TO_IMPORT = "Eine Gruppe oder ein Tool zum Importieren auswählen"
120 | WARNING_TOOL_EXISTS_IN_GROUP = "Der Tool-Name existiert bereits in der Gruppe"
121 |
--------------------------------------------------------------------------------
/toolShelf/locales/strings_en.py:
--------------------------------------------------------------------------------
1 | #
2 |
3 | # ----------------------------------------------------------------------
4 | # Label
5 | # ----------------------------------------------------------------------
6 |
7 | NAME_LABEL = "Tool Shelf"
8 |
9 | # Preferences
10 | LANGUAGE_LABEL = "Language (Requires Restart)"
11 |
12 | ADD_GROUP_LABEL = "Add Group"
13 | ADD_TOOL_LABEL = "Add Tool"
14 | ADDON_TOGGLE_LABEL = "Add-on Toggle"
15 | ADDON_LABEL = "Add-on"
16 | AFTER_GROUP_LABEL = "After Group"
17 | AFTER_TOOL_LABEL = "After Tool"
18 | APPLY_ORDER_LABEL = "Apply Order"
19 | AUTO_EXPAND_LABEL = "Auto Expand"
20 | COMMAND_LABEL = "Command"
21 | COMMANDS_LABEL = "Commands"
22 | DELETE_LABEL = "Delete"
23 | EDIT_LABEL = "Edit"
24 | EDIT_SET_LABEL = "Edit Set"
25 | GROUP_LABEL = "Group"
26 | ICON_LABEL = "Icon"
27 | ICONS_LABEL = "Icons"
28 | ICON_ONLY_LABEL = "Icon Only"
29 | IMPORT_LABEL = "Import"
30 | LABELS_LABEL = "Labels"
31 | MOVE_ITEM_DOWN_LABEL = "Move Item Down"
32 | MOVE_ITEM_UP_LABEL = "Move Item Up"
33 | MODE_LABEL = "Mode"
34 | MOVE_DOWN_LABEL = "Move Down"
35 | MOVE_UP_LABEL = "Move Up"
36 | NEW_GROUP_LABEL = "New Group"
37 | NEW_NAME_LABEL = "Name"
38 | NEW_SET_LABEL = "New Set"
39 | PROPERTY_LABEL = "Property"
40 | PROPERTY_VALUE_LABEL = "Value"
41 | PROPERTY_CALLBACK_LABEL = "Command As Callback"
42 | ROW_BUTTONS_LABEL = "Row Buttons"
43 | SELECT_ITEM_LABEL = "––– Select –––"
44 | SET_NAME_LABEL = "Set Name"
45 | SETUP_LABEL = "Setup"
46 | TOOL_LABEL = "Tool"
47 | TOOLTIP_LABEL = "Tooltip"
48 | VIEW_COMMAND_LABEL = "View Command"
49 |
50 | # ----------------------------------------------------------------------
51 | # Description/Annotation
52 | # ----------------------------------------------------------------------
53 |
54 | # Preferences
55 | ANN_LANGUAGE = "The language for properties and messages"
56 |
57 | ANN_ADDON = "The add-on to create a toggle button for"
58 | ANN_COMMAND = ("The command string for the button.\n"
59 | "For simple commands the bpy import is added automatically.\n"
60 | "Leave empty to get the content from the text editor")
61 | ANN_EXECUTE_MODE = "Execute the current mode"
62 | ANN_EXPAND = "Set the group to be expanded by default"
63 | ANN_IMPORT_FILE = "The configuration file to import groups or tools from"
64 | ANN_IMPORT_ITEM = "The group or tool to import"
65 | ANN_GROUP = ("The group to add the tool to.\n"
66 | "A new group it will be created after the last or after the selected group item")
67 | ANN_ICON = ("The name of the icon file with 32x32 pixel or a default Blender icon identifier "
68 | "enclosed in single quotes or a unicode character")
69 | ANN_ICON_ONLY = "Only show the icon instead of the button label"
70 | ANN_ITEM_ADD_NEW = "Add a new group or tool"
71 | ANN_ITEM_DELETE_EXISTING = "Delete an existing group or button"
72 | ANN_ITEM_DISPLAY_COMMAND = "Display a button command in the text editor"
73 | ANN_ITEM_IMPORT = "Import a group or tool from a configuration file"
74 | ANN_ITEM_OVERWRITE = ("Overwrite the command of an existing button.\nUse the asterisk * "
75 | "symbol to keep existing settings")
76 | ANN_ITEM_REORDER = "Reorder groups and buttons"
77 | ANN_MODE = "Switch the editing mode"
78 | ANN_NAME = ("The name of the group or tool.\n"
79 | "It needs to be unique in lowercase across all groups")
80 | ANN_NEW_GROUP = "Add a new group instead of a new tool"
81 | ANN_NEW_SET = "Add a new tool set"
82 | ANN_PROPERTY = "Add a numeric, boolean or string property to the tool"
83 | ANN_PROPERTY_NAME = "The name of the property"
84 | ANN_PROPERTY_VALUE = ("The default value for the property. To define minimum and maximum values "
85 | "use the format: value, value, value")
86 | ANN_PROPERTY_CALLBACK = ("Use the command string as the body of a callback function for all "
87 | "properties with their update option set")
88 | ANN_SELECT_GROUP = "Select a group"
89 | ANN_SET_COLUMNS = "The number of buttons in a row"
90 | ANN_SET_NAME = "The name of the button set"
91 | ANN_TOGGLE_ADDON = "Create a button to enable or disable an add-on"
92 | ANN_TOOL = "The tool command to edit"
93 | ANN_TOOLTIP = "The tooltip for the button"
94 |
95 | # ----------------------------------------------------------------------
96 | # Info
97 | # ----------------------------------------------------------------------
98 |
99 |
100 | # ----------------------------------------------------------------------
101 | # Warning/Error
102 | # ----------------------------------------------------------------------
103 |
104 | WARNING_ADDON_INCOMPATIBLE = "The add-on is incompatible and has to be enabled/disabled from the preferences: "
105 | WARNING_ADDON_MISSING = "An add-on with this name doesn't exist: "
106 | WARNING_BRACKETS_INCOMPLETE = "The brackets for the properties are incomplete"
107 | WARNING_GROUP_NAME_EXISTS = "The group name already exists"
108 | WARNING_IMAGE_MISSING = "The image doesn't exist in the path: "
109 | WARNING_LABEL_COMMAND_MISMATCH = "The number of tool labels and commands does not match"
110 | WARNING_NO_GROUP_SELECTED = "No group selected"
111 | WARNING_NO_NAME = "No name defined"
112 | WARNING_NO_TEXT_EDITOR = "No text editor open to get the tool command"
113 | WARNING_NO_TEXT_EDITOR_TO_VIEW = "No text editor open to view the command"
114 | WARNING_NO_TOOL_IN_GROUP = "No tool exists in the selected group"
115 | WARNING_NO_TOOL_SELECTED = "No tool selected"
116 | WARNING_PROPERTY_NAME_MISSING = "A property name and/or default value is missing"
117 | WARNING_PROPERTY_VALUE_MISMATCH = "The number of properties and values does not match"
118 | WARNING_SELECT_CONFIG = "Select a valid configuration file"
119 | WARNING_SELECT_TO_IMPORT = "Select a group or tool to import"
120 | WARNING_TOOL_EXISTS_IN_GROUP = "The tool name already exists in the group"
121 |
--------------------------------------------------------------------------------
/toolShelf/locales/strings_es.py:
--------------------------------------------------------------------------------
1 | #
2 |
3 | # ----------------------------------------------------------------------
4 | # Label
5 | # ----------------------------------------------------------------------
6 |
7 | NAME_LABEL = "Tool Shelf"
8 |
9 | # Preferences
10 | LANGUAGE_LABEL = "Idioma (Requiere reinicio)"
11 |
12 | ADD_GROUP_LABEL = "Agregar Grupo"
13 | ADD_TOOL_LABEL = "Agregar Herramienta"
14 | ADDON_TOGGLE_LABEL = "Alternar Add-on"
15 | ADDON_LABEL = "Add-on"
16 | AFTER_GROUP_LABEL = "Después del Grupo"
17 | AFTER_TOOL_LABEL = "Después de la Herramienta"
18 | APPLY_ORDER_LABEL = "Orden de Aplicación"
19 | AUTO_EXPAND_LABEL = "Expansión Automática"
20 | COMMAND_LABEL = "Comando"
21 | COMMANDS_LABEL = "Comandos"
22 | DELETE_LABEL = "Eliminar"
23 | EDIT_LABEL = "Editar"
24 | EDIT_SET_LABEL = "Editar Conjunto"
25 | GROUP_LABEL = "Grupo"
26 | ICON_LABEL = "Ícono"
27 | ICONS_LABEL = "Íconos"
28 | ICON_ONLY_LABEL = "Solo Ícono"
29 | IMPORT_LABEL = "Importar"
30 | LABELS_LABEL = "Etiquetas"
31 | MOVE_ITEM_DOWN_LABEL = "Mover Elemento Abajo"
32 | MOVE_ITEM_UP_LABEL = "Mover Elemento Arriba"
33 | MODE_LABEL = "Modo"
34 | MOVE_DOWN_LABEL = "Mover Abajo"
35 | MOVE_UP_LABEL = "Mover Arriba"
36 | NEW_GROUP_LABEL = "Nuevo Grupo"
37 | NEW_NAME_LABEL = "Nombre"
38 | NEW_SET_LABEL = "Nuevo Conjunto"
39 | PROPERTY_LABEL = "Propiedad"
40 | PROPERTY_VALUE_LABEL = "Valor"
41 | PROPERTY_CALLBACK_LABEL = "Comando como Devolución de Llamada"
42 | ROW_BUTTONS_LABEL = "Botones de Fila"
43 | SELECT_ITEM_LABEL = "––– Seleccionar –––"
44 | SET_NAME_LABEL = "Nombre del Conjunto"
45 | SETUP_LABEL = "Configuración"
46 | TOOL_LABEL = "Herramienta"
47 | TOOLTIP_LABEL = "Tooltip"
48 | VIEW_COMMAND_LABEL = "Ver Comando"
49 |
50 | # ----------------------------------------------------------------------
51 | # Description/Annotation
52 | # ----------------------------------------------------------------------
53 |
54 | # Preferences
55 | ANN_LANGUAGE = "El idioma para propiedades y mensajes"
56 |
57 | ANN_ADDON = "El Add-on para crear un botón de alternancia"
58 | ANN_COMMAND = ("La cadena de comando para el botón.\n"
59 | "Para comandos simples, la importación bpy se agrega automáticamente.\n"
60 | "Deje en blanco para obtener el contenido del editor de texto")
61 | ANN_EXECUTE_MODE = "Ejecutar el modo actual"
62 | ANN_EXPAND = "Establecer el grupo para que se expanda por defecto"
63 | ANN_IMPORT_FILE = ("El archivo de configuración para importar grupos o "
64 | "herramientas desde")
65 | ANN_IMPORT_ITEM = "El grupo o herramienta a importar"
66 | ANN_GROUP = ("El grupo al que agregar la herramienta.\n"
67 | "Se creará un nuevo grupo después del último o después del elemento de grupo seleccionado")
68 | ANN_ICON = ("El nombre del archivo de ícono con 32x32 píxeles o un identificador de ícono de Blender predeterminado "
69 | "encerrado en comillas simples o un carácter Unicode")
70 | ANN_ICON_ONLY = "Mostrar solo el ícono en lugar de la etiqueta del botón"
71 | ANN_ITEM_ADD_NEW = "Agregar un nuevo grupo o herramienta"
72 | ANN_ITEM_DELETE_EXISTING = "Eliminar un grupo o botón existente"
73 | ANN_ITEM_DISPLAY_COMMAND = "Mostrar un comando de botón en el editor de texto"
74 | ANN_ITEM_IMPORT = "Importar un grupo o herramienta desde un archivo de configuración"
75 | ANN_ITEM_OVERWRITE = ("Sobrescribir el comando de un botón existente.\nUse el símbolo asterisco * "
76 | "para mantener la configuración existente")
77 | ANN_ITEM_REORDER = "Reordenar grupos y botones"
78 | ANN_MODE = "Cambiar el modo de edición"
79 | ANN_NAME = ("El nombre del grupo o herramienta.\n"
80 | "Debe ser único en minúsculas en todos los grupos")
81 | ANN_NEW_GROUP = "Agregar un nuevo grupo en lugar de una nueva herramienta"
82 | ANN_NEW_SET = "Agregar un nuevo conjunto de herramientas"
83 | ANN_PROPERTY = "Agregar una propiedad numérica, booleana o de cadena a la herramienta"
84 | ANN_PROPERTY_NAME = "El nombre de la propiedad"
85 | ANN_PROPERTY_VALUE = ("El valor predeterminado de la propiedad. Para definir valores mínimo y máximo "
86 | "utilice el formato: valor, valor, valor")
87 | ANN_PROPERTY_CALLBACK = ("Usar la cadena de comando como cuerpo de una función de devolución de llamada para todas las "
88 | "propiedades con su opción de actualización establecida")
89 | ANN_SELECT_GROUP = "Seleccionar un grupo"
90 | ANN_SET_COLUMNS = "El número de botones en una fila"
91 | ANN_SET_NAME = "El nombre del conjunto de botones"
92 | ANN_TOGGLE_ADDON = "Crear un botón para habilitar o deshabilitar un Add-on"
93 | ANN_TOOL = "El comando de herramienta a editar"
94 | ANN_TOOLTIP = "El tooltip del botón"
95 |
96 | # ----------------------------------------------------------------------
97 | # Info
98 | # ----------------------------------------------------------------------
99 |
100 |
101 | # ----------------------------------------------------------------------
102 | # Warning/Error
103 | # ----------------------------------------------------------------------
104 |
105 | WARNING_ADDON_INCOMPATIBLE = "El Add-on no es compatible y debe habilitarse/deshabilitarse desde las preferencias: "
106 | WARNING_ADDON_MISSING = "No existe un Add-on con este nombre: "
107 | WARNING_BRACKETS_INCOMPLETE = "Los corchetes de las propiedades están incompletos"
108 | WARNING_GROUP_NAME_EXISTS = "El nombre de grupo ya existe"
109 | WARNING_IMAGE_MISSING = "La imagen no existe en la ruta: "
110 | WARNING_LABEL_COMMAND_MISMATCH = "El número de etiquetas y comandos de herramienta no coincide"
111 | WARNING_NO_GROUP_SELECTED = "Ningún grupo seleccionado"
112 | WARNING_NO_NAME = "No se ha definido ningún nombre"
113 | WARNING_NO_TEXT_EDITOR = "No hay un editor de texto abierto para obtener el comando de herramienta"
114 | WARNING_NO_TEXT_EDITOR_TO_VIEW = "No hay un editor de texto abierto para ver el comando"
115 | WARNING_NO_TOOL_IN_GROUP = "No existe una herramienta en el grupo seleccionado"
116 | WARNING_NO_TOOL_SELECTED = "Ninguna herramienta seleccionada"
117 | WARNING_PROPERTY_NAME_MISSING = "Falta el nombre de una propiedad y/o valor predeterminado"
118 | WARNING_PROPERTY_VALUE_MISMATCH = "El número de propiedades y valores no coincide"
119 | WARNING_SELECT_CONFIG = "Seleccione un archivo de configuración válido"
120 | WARNING_SELECT_TO_IMPORT = "Seleccione un grupo o herramienta para importar"
121 | WARNING_TOOL_EXISTS_IN_GROUP = "El nombre de la herramienta ya existe en el grupo"
122 |
--------------------------------------------------------------------------------
/toolShelf/locales/strings_fr.py:
--------------------------------------------------------------------------------
1 | #
2 |
3 | # ----------------------------------------------------------------------
4 | # Label
5 | # ----------------------------------------------------------------------
6 |
7 | NAME_LABEL = "Tool Shelf"
8 |
9 | # Preferences
10 | LANGUAGE_LABEL = "Langue (Nécessite un redémarrage)"
11 |
12 | ADD_GROUP_LABEL = "Ajouter un Groupe"
13 | ADD_TOOL_LABEL = "Ajouter un Outil"
14 | ADDON_TOGGLE_LABEL = "Basculer l'Add-on"
15 | ADDON_LABEL = "Add-on"
16 | AFTER_GROUP_LABEL = "Après le Groupe"
17 | AFTER_TOOL_LABEL = "Après l'Outil"
18 | APPLY_ORDER_LABEL = "Ordre d'Application"
19 | AUTO_EXPAND_LABEL = "Expansion Automatique"
20 | COMMAND_LABEL = "Commande"
21 | COMMANDS_LABEL = "Commandes"
22 | DELETE_LABEL = "Supprimer"
23 | EDIT_LABEL = "Éditer"
24 | EDIT_SET_LABEL = "Éditer l'Ensemble"
25 | GROUP_LABEL = "Groupe"
26 | ICON_LABEL = "Icône"
27 | ICONS_LABEL = "Icônes"
28 | ICON_ONLY_LABEL = "Icône Seule"
29 | IMPORT_LABEL = "Importer"
30 | LABELS_LABEL = "Étiquettes"
31 | MOVE_ITEM_DOWN_LABEL = "Déplacer l'Élément vers le Bas"
32 | MOVE_ITEM_UP_LABEL = "Déplacer l'Élément vers le Haut"
33 | MODE_LABEL = "Mode"
34 | MOVE_DOWN_LABEL = "Descendre"
35 | MOVE_UP_LABEL = "Monter"
36 | NEW_GROUP_LABEL = "Nouveau Groupe"
37 | NEW_NAME_LABEL = "Nom"
38 | NEW_SET_LABEL = "Nouvel Ensemble"
39 | PROPERTY_LABEL = "Propriété"
40 | PROPERTY_VALUE_LABEL = "Valeur"
41 | PROPERTY_CALLBACK_LABEL = "Commande comme Rappel"
42 | ROW_BUTTONS_LABEL = "Boutons de Ligne"
43 | SELECT_ITEM_LABEL = "––– Sélectionner –––"
44 | SET_NAME_LABEL = "Nom de l'Ensemble"
45 | SETUP_LABEL = "Configuration"
46 | TOOL_LABEL = "Outil"
47 | TOOLTIP_LABEL = "Info-bulle"
48 | VIEW_COMMAND_LABEL = "Voir la Commande"
49 |
50 | # ----------------------------------------------------------------------
51 | # Description/Annotation
52 | # ----------------------------------------------------------------------
53 |
54 | # Preferences
55 | ANN_LANGUAGE = "La langue des propriétés et des messages"
56 |
57 | ANN_ADDON = "L'Add-on pour créer un bouton basculant"
58 | ANN_COMMAND = ("La chaîne de commande pour le bouton.\n"
59 | "Pour les commandes simples, l'importation bpy est ajoutée automatiquement.\n"
60 | "Laissez vide pour obtenir le contenu de l'éditeur de texte")
61 | ANN_EXECUTE_MODE = "Exécuter le mode actuel"
62 | ANN_EXPAND = "Définir le groupe comme étant déployé par défaut"
63 | ANN_IMPORT_FILE = ("Le fichier de configuration pour importer des groupes ou "
64 | "des outils à partir de")
65 | ANN_IMPORT_ITEM = "Le groupe ou l'outil à importer"
66 | ANN_GROUP = ("Le groupe où ajouter l'outil.\n"
67 | "Un nouveau groupe sera créé après le dernier ou après l'élément de groupe sélectionné")
68 | ANN_ICON = ("Le nom du fichier d'icône avec une résolution de 32x32 pixels ou un identifiant d'icône Blender par défaut "
69 | "encadré par des guillemets simples ou un caractère Unicode")
70 | ANN_ICON_ONLY = "Afficher uniquement l'icône au lieu de l'étiquette du bouton"
71 | ANN_ITEM_ADD_NEW = "Ajouter un nouveau groupe ou un nouvel outil"
72 | ANN_ITEM_DELETE_EXISTING = "Supprimer un groupe ou un bouton existant"
73 | ANN_ITEM_DISPLAY_COMMAND = "Afficher une commande de bouton dans l'éditeur de texte"
74 | ANN_ITEM_IMPORT = "Importer un groupe ou un outil à partir d'un fichier de configuration"
75 | ANN_ITEM_OVERWRITE = ("Écraser la commande d'un bouton existant.\nUtilisez le symbole astérisque * "
76 | "pour conserver les paramètres existants")
77 | ANN_ITEM_REORDER = "Réorganiser les groupes et les boutons"
78 | ANN_MODE = "Changer le mode d'édition"
79 | ANN_NAME = ("Le nom du groupe ou de l'outil.\n"
80 | "Il doit être unique en minuscules dans tous les groupes")
81 | ANN_NEW_GROUP = "Ajouter un nouveau groupe au lieu d'un nouvel outil"
82 | ANN_NEW_SET = "Ajouter un nouvel ensemble d'outils"
83 | ANN_PROPERTY = "Ajouter une propriété numérique, booléenne ou de chaîne à l'outil"
84 | ANN_PROPERTY_NAME = "Le nom de la propriété"
85 | ANN_PROPERTY_VALUE = ("La valeur par défaut de la propriété. Pour définir des valeurs minimales et maximales, "
86 | "utilisez le format : valeur, valeur, valeur")
87 | ANN_PROPERTY_CALLBACK = ("Utiliser la chaîne de commande comme corps d'une fonction de rappel pour toutes les "
88 | "propriétés avec leur option de mise à jour définie")
89 | ANN_SELECT_GROUP = "Sélectionner un groupe"
90 | ANN_SET_COLUMNS = "Le nombre de boutons par ligne"
91 | ANN_SET_NAME = "Le nom de l'ensemble de boutons"
92 | ANN_TOGGLE_ADDON = "Créer un bouton pour activer ou désactiver un Add-on"
93 | ANN_TOOL = "La commande de l'outil à éditer"
94 | ANN_TOOLTIP = "L'info-bulle du bouton"
95 |
96 | # ----------------------------------------------------------------------
97 | # Info
98 | # ----------------------------------------------------------------------
99 |
100 |
101 | # ----------------------------------------------------------------------
102 | # Warning/Error
103 | # ----------------------------------------------------------------------
104 |
105 | WARNING_ADDON_INCOMPATIBLE = "L'Add-on n'est pas compatible et doit être activé/désactivé depuis les préférences : "
106 | WARNING_ADDON_MISSING = "Aucun Add-on portant ce nom n'existe : "
107 | WARNING_BRACKETS_INCOMPLETE = "Les parenthèses pour les propriétés ne sont pas complètes"
108 | WARNING_GROUP_NAME_EXISTS = "Le nom du groupe existe déjà"
109 | WARNING_IMAGE_MISSING = "L'image n'existe pas dans le chemin : "
110 | WARNING_LABEL_COMMAND_MISMATCH = "Le nombre d'étiquettes d'outils et de commandes ne correspond pas"
111 | WARNING_NO_GROUP_SELECTED = "Aucun groupe sélectionné"
112 | WARNING_NO_NAME = "Aucun nom défini"
113 | WARNING_NO_TEXT_EDITOR = "Aucun éditeur de texte ouvert pour obtenir la commande de l'outil"
114 | WARNING_NO_TEXT_EDITOR_TO_VIEW = "Aucun éditeur de texte ouvert pour afficher la commande"
115 | WARNING_NO_TOOL_IN_GROUP = "Aucun outil n'existe dans le groupe sélectionné"
116 | WARNING_NO_TOOL_SELECTED = "Aucun outil sélectionné"
117 | WARNING_PROPERTY_NAME_MISSING = "Un nom de propriété et/ou une valeur par défaut sont manquants"
118 | WARNING_PROPERTY_VALUE_MISMATCH = "Le nombre de propriétés et de valeurs ne correspond pas"
119 | WARNING_SELECT_CONFIG = "Sélectionnez un fichier de configuration valide"
120 | WARNING_SELECT_TO_IMPORT = "Sélectionnez un groupe ou un outil à importer"
121 | WARNING_TOOL_EXISTS_IN_GROUP = "Le nom de l'outil existe déjà dans le groupe"
122 |
--------------------------------------------------------------------------------
/toolShelf/locales/strings_jp.py:
--------------------------------------------------------------------------------
1 | #
2 |
3 | # ----------------------------------------------------------------------
4 | # Label
5 | # ----------------------------------------------------------------------
6 |
7 | NAME_LABEL = "Tool Shelf"
8 |
9 | # Preferences
10 | LANGUAGE_LABEL = "言語(再起動が必要)"
11 |
12 | ADD_GROUP_LABEL = "グループを追加"
13 | ADD_TOOL_LABEL = "ツールを追加"
14 | ADDON_TOGGLE_LABEL = "アドオン トグル"
15 | ADDON_LABEL = "アドオン"
16 | AFTER_GROUP_LABEL = "グループの後"
17 | AFTER_TOOL_LABEL = "ツールの後"
18 | APPLY_ORDER_LABEL = "適用順"
19 | AUTO_EXPAND_LABEL = "自動展開"
20 | COMMAND_LABEL = "コマンド"
21 | COMMANDS_LABEL = "コマンド"
22 | DELETE_LABEL = "削除"
23 | EDIT_LABEL = "編集"
24 | EDIT_SET_LABEL = "セットを編集"
25 | GROUP_LABEL = "グループ"
26 | ICON_LABEL = "アイコン"
27 | ICONS_LABEL = "アイコン"
28 | ICON_ONLY_LABEL = "アイコンのみ"
29 | IMPORT_LABEL = "インポート"
30 | LABELS_LABEL = "ラベル"
31 | MOVE_ITEM_DOWN_LABEL = "アイテムを下へ移動"
32 | MOVE_ITEM_UP_LABEL = "アイテムを上へ移動"
33 | MODE_LABEL = "モード"
34 | MOVE_DOWN_LABEL = "下へ移動"
35 | MOVE_UP_LABEL = "上へ移動"
36 | NEW_GROUP_LABEL = "新規グループ"
37 | NEW_NAME_LABEL = "名前"
38 | NEW_SET_LABEL = "新規セット"
39 | PROPERTY_LABEL = "プロパティ"
40 | PROPERTY_VALUE_LABEL = "値"
41 | PROPERTY_CALLBACK_LABEL = "コールバックとしてコマンドを使用"
42 | ROW_BUTTONS_LABEL = "行ボタン"
43 | SELECT_ITEM_LABEL = "––– 選択 –––"
44 | SET_NAME_LABEL = "セット名"
45 | SETUP_LABEL = "セットアップ"
46 | TOOL_LABEL = "ツール"
47 | TOOLTIP_LABEL = "ツールチップ"
48 | VIEW_COMMAND_LABEL = "コマンドを表示"
49 |
50 | # ----------------------------------------------------------------------
51 | # Description/Annotation
52 | # ----------------------------------------------------------------------
53 |
54 | # Preferences
55 | ANN_LANGUAGE = "プロパティとメッセージの言語"
56 |
57 | ANN_ADDON = "トグルボタンを作成するアドオン"
58 | ANN_COMMAND = ("ボタンのコマンド文字列。\n"
59 | "簡単なコマンドの場合、bpyインポートが自動的に追加されます。\n"
60 | "空白の場合はテキストエディタから内容を取得します")
61 | ANN_EXECUTE_MODE = "現在のモードを実行"
62 | ANN_EXPAND = "グループをデフォルトで展開するように設定"
63 | ANN_IMPORT_FILE = "グループまたはツールをインポートする構成ファイル"
64 | ANN_IMPORT_ITEM = "インポートするグループまたはツール"
65 | ANN_GROUP = ("ツールを追加するグループ。\n"
66 | "新しいグループは、最後または選択したグループアイテムの後に作成されます")
67 | ANN_ICON = ("32x32ピクセルのアイコンファイルの名前またはデフォルトのBlenderアイコン識別子、"
68 | "シングルクォートで囲まれたUnicode文字")
69 | ANN_ICON_ONLY = "ボタンのラベルの代わりにアイコンのみ表示"
70 | ANN_ITEM_ADD_NEW = "新しいグループまたはツールを追加"
71 | ANN_ITEM_DELETE_EXISTING = "既存のグループまたはボタンを削除"
72 | ANN_ITEM_DISPLAY_COMMAND = "ボタンコマンドをテキストエディタに表示"
73 | ANN_ITEM_IMPORT = "構成ファイルからグループまたはツールをインポート"
74 | ANN_ITEM_OVERWRITE = ("既存のボタンのコマンドを上書きします。\n"
75 | "存在する設定を保持するには、アスタリスク*記号を使用します")
76 | ANN_ITEM_REORDER = "グループとボタンの並べ替え"
77 | ANN_MODE = "編集モードを切り替え"
78 | ANN_NAME = ("グループまたはツールの名前。\n"
79 | "すべてのグループで一意の小文字である必要があります")
80 | ANN_NEW_GROUP = "新しいツールではなく新しいグループを追加"
81 | ANN_NEW_SET = "新しいツールセットを追加"
82 | ANN_PROPERTY = "ツールに数値、ブール値、または文字列プロパティを追加"
83 | ANN_PROPERTY_NAME = "プロパティの名前"
84 | ANN_PROPERTY_VALUE = ("プロパティのデフォルト値。最小値と最大値を定義するには、"
85 | "値、値、値の形式を使用します")
86 | ANN_PROPERTY_CALLBACK = ("更新オプションが設定されたすべてのプロパティに対して、"
87 | "コマンド文字列をコールバック関数の本文として使用します")
88 | ANN_SELECT_GROUP = "グループを選択"
89 | ANN_SET_COLUMNS = "1行のボタンの数"
90 | ANN_SET_NAME = "ボタンセットの名前"
91 | ANN_TOGGLE_ADDON = "アドオンを有効または無効にするボタンを作成"
92 | ANN_TOOL = "編集するツールコマンド"
93 | ANN_TOOLTIP = "ボタンのツールチップ"
94 |
95 | # ----------------------------------------------------------------------
96 | # Info
97 | # ----------------------------------------------------------------------
98 |
99 |
100 | # ----------------------------------------------------------------------
101 | # Warning/Error
102 | # ----------------------------------------------------------------------
103 |
104 | WARNING_ADDON_INCOMPATIBLE = "このアドオンは互換性がなく、設定から有効または無効にする必要があります:"
105 | WARNING_ADDON_MISSING = "この名前のアドオンは存在しません:"
106 | WARNING_BRACKETS_INCOMPLETE = "プロパティの括弧が不完全です"
107 | WARNING_GROUP_NAME_EXISTS = "グループ名はすでに存在します"
108 | WARNING_IMAGE_MISSING = "パスに画像が存在しません:"
109 | WARNING_LABEL_COMMAND_MISMATCH = "ツールのラベルとコマンドの数が一致しません"
110 | WARNING_NO_GROUP_SELECTED = "グループが選択されていません"
111 | WARNING_NO_NAME = "名前が定義されていません"
112 | WARNING_NO_TEXT_EDITOR = "ツールコマンドを取得するためのテキストエディタが開いていません"
113 | WARNING_NO_TEXT_EDITOR_TO_VIEW = "コマンドを表示するためのテキストエディタが開いていません"
114 | WARNING_NO_TOOL_IN_GROUP = "選択したグループにツールが存在しません"
115 | WARNING_NO_TOOL_SELECTED = "ツールが選択されていません"
116 | WARNING_PROPERTY_NAME_MISSING = "プロパティ名と/またはデフォルト値が不足しています"
117 | WARNING_PROPERTY_VALUE_MISMATCH = "プロパティと値の数が一致しません"
118 | WARNING_SELECT_CONFIG = "有効な構成ファイルを選択してください"
119 | WARNING_SELECT_TO_IMPORT = "インポートするグループまたはツールを選択してください"
120 | WARNING_TOOL_EXISTS_IN_GROUP = "ツール名は既にグループ内に存在します"
121 |
--------------------------------------------------------------------------------
/toolShelf/locales/strings_ko.py:
--------------------------------------------------------------------------------
1 | #
2 |
3 | # ----------------------------------------------------------------------
4 | # Label
5 | # ----------------------------------------------------------------------
6 |
7 | NAME_LABEL = "Tool Shelf"
8 |
9 | # Preferences
10 | LANGUAGE_LABEL = "언어 (재시작 필요)"
11 |
12 | ADD_GROUP_LABEL = "그룹 추가"
13 | ADD_TOOL_LABEL = "도구 추가"
14 | ADDON_TOGGLE_LABEL = "애드온 전환"
15 | ADDON_LABEL = "애드온"
16 | AFTER_GROUP_LABEL = "그룹 이후"
17 | AFTER_TOOL_LABEL = "도구 이후"
18 | APPLY_ORDER_LABEL = "적용 순서"
19 | AUTO_EXPAND_LABEL = "자동 확장"
20 | COMMAND_LABEL = "명령"
21 | COMMANDS_LABEL = "명령들"
22 | DELETE_LABEL = "삭제"
23 | EDIT_LABEL = "편집"
24 | EDIT_SET_LABEL = "집합 편집"
25 | GROUP_LABEL = "그룹"
26 | ICON_LABEL = "아이콘"
27 | ICONS_LABEL = "아이콘들"
28 | ICON_ONLY_LABEL = "아이콘만"
29 | IMPORT_LABEL = "가져오기"
30 | LABELS_LABEL = "라벨"
31 | MOVE_ITEM_DOWN_LABEL = "아이템 아래로 이동"
32 | MOVE_ITEM_UP_LABEL = "아이템 위로 이동"
33 | MODE_LABEL = "모드"
34 | MOVE_DOWN_LABEL = "아래로 이동"
35 | MOVE_UP_LABEL = "위로 이동"
36 | NEW_GROUP_LABEL = "새 그룹"
37 | NEW_NAME_LABEL = "이름"
38 | NEW_SET_LABEL = "새로운 집합"
39 | PROPERTY_LABEL = "프로퍼티"
40 | PROPERTY_VALUE_LABEL = "값"
41 | PROPERTY_CALLBACK_LABEL = "콜백으로 명령 사용"
42 | ROW_BUTTONS_LABEL = "행 버튼들"
43 | SELECT_ITEM_LABEL = "––– 선택 –––"
44 | SET_NAME_LABEL = "집합 이름"
45 | SETUP_LABEL = "설정"
46 | TOOL_LABEL = "도구"
47 | TOOLTIP_LABEL = "툴팁"
48 | VIEW_COMMAND_LABEL = "명령 보기"
49 |
50 | # ----------------------------------------------------------------------
51 | # Description/Annotation
52 | # ----------------------------------------------------------------------
53 |
54 | # Preferences
55 | ANN_LANGUAGE = "속성과 메시지에 대한 언어"
56 |
57 | ANN_ADDON = "전환 버튼을 생성하는 애드온"
58 | ANN_COMMAND = ("버튼에 대한 명령 문자열.\n"
59 | "단순한 명령의 경우 자동으로 bpy 가져오기가 추가됩니다.\n"
60 | "내용을 얻으려면 비워두십시오.")
61 | ANN_EXECUTE_MODE = "현재 모드 실행"
62 | ANN_EXPAND = "그룹을 기본적으로 확장하도록 설정"
63 | ANN_IMPORT_FILE = "그룹 또는 도구를 가져 오기 위한 설정 파일"
64 | ANN_IMPORT_ITEM = "가져올 그룹 또는 도구"
65 | ANN_GROUP = ("도구를 추가 할 그룹.\n"
66 | "마지막 또는 선택한 그룹 항목 이후에 새로운 그룹이 만들어집니다.")
67 | ANN_ICON = "32x32 픽셀 아이콘 파일 이름 또는 작은 따옴표로 묶인 Blender 기본 아이콘 식별자 또는 유니코드 문자"
68 | ANN_ICON_ONLY = "버튼 레이블 대신 아이콘만 표시"
69 | ANN_ITEM_ADD_NEW = "새로운 그룹 또는 도구 추가"
70 | ANN_ITEM_DELETE_EXISTING = "기존 그룹 또는 버튼 삭제"
71 | ANN_ITEM_DISPLAY_COMMAND = "텍스트 편집기에서 버튼 명령 표시"
72 | ANN_ITEM_IMPORT = "구성 파일에서 그룹 또는 도구 가져 오기"
73 | ANN_ITEM_OVERWRITE = "기존 버튼의 명령 덮어 쓰기.\n기존 설정 유지에는 별표 *를 사용하십시오."
74 | ANN_ITEM_REORDER = "그룹 및 버튼 재정렬"
75 | ANN_MODE = "편집 모드 전환"
76 | ANN_NAME = ("그룹 또는 도구의 이름.\n"
77 | "모든 그룹에서 고유해야합니다.")
78 | ANN_NEW_GROUP = "새 도구 대신 새 그룹 추가"
79 | ANN_NEW_SET = "새로운 도구 세트 추가"
80 | ANN_PROPERTY = "도구에 숫자, 부울 또는 문자열 속성 추가"
81 | ANN_PROPERTY_NAME = "속성 이름"
82 | ANN_PROPERTY_VALUE = "속성의 기본 값. 최소값 및 최대값을 정의하려면 다음 형식을 사용하십시오 : 값, 값, 값"
83 | ANN_PROPERTY_CALLBACK = "모든 속성에 대해 업데이트 옵션을 갖는 콜백 함수의 본문으로 명령 문자열을 사용하십시오."
84 | ANN_SELECT_GROUP = "그룹 선택"
85 | ANN_SET_COLUMNS = "한 행에 버튼 수"
86 | ANN_SET_NAME = "버튼 세트 이름"
87 | ANN_TOGGLE_ADDON = "애드온을 활성화 또는 비활성화하기 위한 버튼 생성"
88 | ANN_TOOL = "편집 할 도구 명령"
89 | ANN_TOOLTIP = "버튼의 툴팁"
90 |
91 | # ----------------------------------------------------------------------
92 | # Info
93 | # ----------------------------------------------------------------------
94 |
95 |
96 | # ----------------------------------------------------------------------
97 | # Warning/Error
98 | # ----------------------------------------------------------------------
99 |
100 | WARNING_ADDON_INCOMPATIBLE = "애드온이 호환되지 않으며 환경 설정에서 활성화/비활성화해야합니다 : "
101 | WARNING_ADDON_MISSING = "이름으로 된 이 애드온이 없습니다: "
102 | WARNING_BRACKETS_INCOMPLETE = "속성의 괄호가 불완전합니다"
103 | WARNING_GROUP_NAME_EXISTS = "그룹 이름이 이미 존재합니다"
104 | WARNING_IMAGE_MISSING = "경로에 이미지가 존재하지 않습니다: "
105 | WARNING_LABEL_COMMAND_MISMATCH = "도구 레이블 및 명령의 수가 일치하지 않습니다"
106 | WARNING_NO_GROUP_SELECTED = "선택한 그룹이 없습니다"
107 | WARNING_NO_NAME = "이름이 정의되지 않았습니다"
108 | WARNING_NO_TEXT_EDITOR = "도구 명령을 가져 오기위한 열린 텍스트 편집기가 없습니다"
109 | WARNING_NO_TEXT_EDITOR_TO_VIEW = "명령을 볼 텍스트 편집기가 열려 있지 않습니다"
110 | WARNING_NO_TOOL_IN_GROUP = "선택한 그룹에 도구가 없습니다"
111 | WARNING_NO_TOOL_SELECTED = "도구가 선택되지 않았습니다"
112 | WARNING_PROPERTY_NAME_MISSING = "속성 이름 및/또는 기본 값이 누락되었습니다"
113 | WARNING_PROPERTY_VALUE_MISMATCH = "속성 및 값의 수가 일치하지 않습니다"
114 | WARNING_SELECT_CONFIG = "유효한 구성 파일을 선택하십시오"
115 | WARNING_SELECT_TO_IMPORT = "가져올 그룹 또는 도구를 선택하십시오"
116 | WARNING_TOOL_EXISTS_IN_GROUP = "그룹에 이미 도구 이름이 있습니다"
117 |
--------------------------------------------------------------------------------
/toolShelf/locales/strings_pt.py:
--------------------------------------------------------------------------------
1 | #
2 |
3 | # ----------------------------------------------------------------------
4 | # Label
5 | # ----------------------------------------------------------------------
6 |
7 | NAME_LABEL = "Tool Shelf"
8 |
9 | # Preferences
10 | LANGUAGE_LABEL = "Idioma (Requer reinício)"
11 |
12 | ADD_GROUP_LABEL = "Adicionar Grupo"
13 | ADD_TOOL_LABEL = "Adicionar Ferramenta"
14 | ADDON_TOGGLE_LABEL = "Alternar Add-on"
15 | ADDON_LABEL = "Add-on"
16 | AFTER_GROUP_LABEL = "Após o Grupo"
17 | AFTER_TOOL_LABEL = "Após a Ferramenta"
18 | APPLY_ORDER_LABEL = "Ordem de Aplicação"
19 | AUTO_EXPAND_LABEL = "Expandir Automaticamente"
20 | COMMAND_LABEL = "Comando"
21 | COMMANDS_LABEL = "Comandos"
22 | DELETE_LABEL = "Apagar"
23 | EDIT_LABEL = "Editar"
24 | EDIT_SET_LABEL = "Editar Conjunto"
25 | GROUP_LABEL = "Grupo"
26 | ICON_LABEL = "Ícone"
27 | ICONS_LABEL = "Ícones"
28 | ICON_ONLY_LABEL = "Somente Ícone"
29 | IMPORT_LABEL = "Importar"
30 | LABELS_LABEL = "Rótulos"
31 | MOVE_ITEM_DOWN_LABEL = "Mover Item para Baixo"
32 | MOVE_ITEM_UP_LABEL = "Mover Item para Cima"
33 | MODE_LABEL = "Modo"
34 | MOVE_DOWN_LABEL = "Mover para Baixo"
35 | MOVE_UP_LABEL = "Mover para Cima"
36 | NEW_GROUP_LABEL = "Novo Grupo"
37 | NEW_NAME_LABEL = "Nome"
38 | NEW_SET_LABEL = "Novo Conjunto"
39 | PROPERTY_LABEL = "Propriedade"
40 | PROPERTY_VALUE_LABEL = "Valor"
41 | PROPERTY_CALLBACK_LABEL = "Comando como Callback"
42 | ROW_BUTTONS_LABEL = "Botões de Linha"
43 | SELECT_ITEM_LABEL = "––– Selecionar –––"
44 | SET_NAME_LABEL = "Nome do Conjunto"
45 | SETUP_LABEL = "Configuração"
46 | TOOL_LABEL = "Ferramenta"
47 | TOOLTIP_LABEL = "Dica de Ferramenta"
48 | VIEW_COMMAND_LABEL = "Ver Comando"
49 |
50 | # ----------------------------------------------------------------------
51 | # Description/Annotation
52 | # ----------------------------------------------------------------------
53 |
54 | # Preferences
55 | ANN_LANGUAGE = "O idioma para propriedades e mensagens"
56 |
57 | ANN_ADDON = "O add-on para criar um botão de alternância"
58 | ANN_COMMAND = ("A string de comando para o botão.\n"
59 | "Para comandos simples, a importação bpy é adicionada automaticamente.\n"
60 | "Deixe em branco para obter o conteúdo do editor de texto")
61 | ANN_EXECUTE_MODE = "Executar o modo atual"
62 | ANN_EXPAND = "Definir o grupo para ser expandido por padrão"
63 | ANN_IMPORT_FILE = ("O arquivo de configuração para importar grupos ou "
64 | "ferramentas a partir de")
65 | ANN_IMPORT_ITEM = "O grupo ou ferramenta a ser importado"
66 | ANN_GROUP = ("O grupo ao qual adicionar a ferramenta.\n"
67 | "Um novo grupo será criado após o último ou após o item de grupo selecionado")
68 | ANN_ICON = ("O nome do arquivo de ícone com 32x32 pixels ou um identificador de ícone padrão do Blender "
69 | "entre aspas simples ou um caractere Unicode")
70 | ANN_ICON_ONLY = "Mostrar apenas o ícone em vez do rótulo do botão"
71 | ANN_ITEM_ADD_NEW = "Adicionar um novo grupo ou ferramenta"
72 | ANN_ITEM_DELETE_EXISTING = "Excluir um grupo ou botão existente"
73 | ANN_ITEM_DISPLAY_COMMAND = "Exibir um comando de botão no editor de texto"
74 | ANN_ITEM_IMPORT = "Importar um grupo ou ferramenta de um arquivo de configuração"
75 | ANN_ITEM_OVERWRITE = ("Sobrescrever o comando de um botão existente.\nUse o asterisco * "
76 | "para manter as configurações existentes")
77 | ANN_ITEM_REORDER = "Reordenar grupos e botões"
78 | ANN_MODE = "Alternar o modo de edição"
79 | ANN_NAME = ("O nome do grupo ou ferramenta.\n"
80 | "Deve ser único em minúsculas em todos os grupos")
81 | ANN_NEW_GROUP = "Adicionar um novo grupo em vez de uma nova ferramenta"
82 | ANN_NEW_SET = "Adicionar um novo conjunto de ferramentas"
83 | ANN_PROPERTY = "Adicionar uma propriedade numérica, booleana ou de string à ferramenta"
84 | ANN_PROPERTY_NAME = "O nome da propriedade"
85 | ANN_PROPERTY_VALUE = ("O valor padrão para a propriedade. Para definir valores mínimo e máximo, "
86 | "use o formato: valor, valor, valor")
87 | ANN_PROPERTY_CALLBACK = ("Usar a string de comando como o corpo de uma função de retorno de chamada para todas as "
88 | "propriedades com sua opção de atualização definida")
89 | ANN_SELECT_GROUP = "Selecionar um grupo"
90 | ANN_SET_COLUMNS = "O número de botões em uma linha"
91 | ANN_SET_NAME = "O nome do conjunto de botões"
92 | ANN_TOGGLE_ADDON = "Criar um botão para ativar ou desativar um add-on"
93 | ANN_TOOL = "O comando da ferramenta a ser editado"
94 | ANN_TOOLTIP = "A dica de ferramenta para o botão"
95 |
96 | # ----------------------------------------------------------------------
97 | # Info
98 | # ----------------------------------------------------------------------
99 |
100 |
101 | # ----------------------------------------------------------------------
102 | # Warning/Error
103 | # ----------------------------------------------------------------------
104 |
105 | WARNING_ADDON_INCOMPATIBLE = "O add-on é incompatível e deve ser ativado/desativado nas preferências: "
106 | WARNING_ADDON_MISSING = "Um add-on com este nome não existe: "
107 | WARNING_BRACKETS_INCOMPLETE = "Os parênteses das propriedades estão incompletos"
108 | WARNING_GROUP_NAME_EXISTS = "O nome do grupo já existe"
109 | WARNING_IMAGE_MISSING = "A imagem não existe no caminho: "
110 | WARNING_LABEL_COMMAND_MISMATCH = "A quantidade de rótulos e comandos de ferramenta não coincide"
111 | WARNING_NO_GROUP_SELECTED = "Nenhum grupo selecionado"
112 | WARNING_NO_NAME = "Nenhum nome definido"
113 | WARNING_NO_TEXT_EDITOR = "Não há um editor de texto aberto para obter o comando da ferramenta"
114 | WARNING_NO_TEXT_EDITOR_TO_VIEW = "Não há um editor de texto aberto para ver o comando"
115 | WARNING_NO_TOOL_IN_GROUP = "Não existe uma ferramenta no grupo selecionado"
116 | WARNING_NO_TOOL_SELECTED = "Nenhuma ferramenta selecionada"
117 | WARNING_PROPERTY_NAME_MISSING = "Falta o nome de uma propriedade e/ou valor padrão"
118 | WARNING_PROPERTY_VALUE_MISMATCH = "A quantidade de propriedades e valores não coincide"
119 | WARNING_SELECT_CONFIG = "Selecione um arquivo de configuração válido"
120 | WARNING_SELECT_TO_IMPORT = "Selecione um grupo ou ferramenta para importar"
121 | WARNING_TOOL_EXISTS_IN_GROUP = "O nome da ferramenta já existe no grupo"
122 |
--------------------------------------------------------------------------------
/toolShelf/locales/strings_zh.py:
--------------------------------------------------------------------------------
1 | #
2 |
3 | # ----------------------------------------------------------------------
4 | # Label
5 | # ----------------------------------------------------------------------
6 |
7 | NAME_LABEL = "Tool Shelf"
8 |
9 | # Preferences
10 | LANGUAGE_LABEL = "语言(需要重新启动)"
11 |
12 | ADD_GROUP_LABEL = "添加组"
13 | ADD_TOOL_LABEL = "添加工具"
14 | ADDON_TOGGLE_LABEL = "插件切换"
15 | ADDON_LABEL = "插件"
16 | AFTER_GROUP_LABEL = "组后"
17 | AFTER_TOOL_LABEL = "工具后"
18 | APPLY_ORDER_LABEL = "应用顺序"
19 | AUTO_EXPAND_LABEL = "自动展开"
20 | COMMAND_LABEL = "命令"
21 | COMMANDS_LABEL = "命令"
22 | DELETE_LABEL = "删除"
23 | EDIT_LABEL = "编辑"
24 | EDIT_SET_LABEL = "编辑设置"
25 | GROUP_LABEL = "组"
26 | ICON_LABEL = "图标"
27 | ICONS_LABEL = "图标"
28 | ICON_ONLY_LABEL = "仅图标"
29 | IMPORT_LABEL = "导入"
30 | LABELS_LABEL = "标签"
31 | MOVE_ITEM_DOWN_LABEL = "下移项目"
32 | MOVE_ITEM_UP_LABEL = "上移项目"
33 | MODE_LABEL = "模式"
34 | MOVE_DOWN_LABEL = "下移"
35 | MOVE_UP_LABEL = "上移"
36 | NEW_GROUP_LABEL = "新建组"
37 | NEW_NAME_LABEL = "名称"
38 | NEW_SET_LABEL = "新建集"
39 | PROPERTY_LABEL = "属性"
40 | PROPERTY_VALUE_LABEL = "值"
41 | PROPERTY_CALLBACK_LABEL = "命令作为回调"
42 | ROW_BUTTONS_LABEL = "行按钮"
43 | SELECT_ITEM_LABEL = "––– 选择 –––"
44 | SET_NAME_LABEL = "集名称"
45 | SETUP_LABEL = "设置"
46 | TOOL_LABEL = "工具"
47 | TOOLTIP_LABEL = "工具提示"
48 | VIEW_COMMAND_LABEL = "查看命令"
49 |
50 | # ----------------------------------------------------------------------
51 | # Description/Annotation
52 | # ----------------------------------------------------------------------
53 |
54 | # Preferences
55 | ANN_LANGUAGE = "属性和消息的语言"
56 |
57 | ANN_ADDON = "创建切换按钮的插件"
58 | ANN_COMMAND = ("按钮的命令字符串。\n"
59 | "对于简单的命令,将自动添加bpy导入。\n"
60 | "留空以从文本编辑器获取内容")
61 | ANN_EXECUTE_MODE = "执行当前模式"
62 | ANN_EXPAND = "将组设置为默认展开"
63 | ANN_IMPORT_FILE = "从中导入组或工具的配置文件"
64 | ANN_IMPORT_ITEM = "要导入的组或工具"
65 | ANN_GROUP = ("要将工具添加到的组。\n"
66 | "新组将在最后一个组或选定的组项目后创建")
67 | ANN_ICON = ("32x32像素图标文件的名称或默认的Blender图标标识符,"
68 | "用单引号括起来或Unicode字符")
69 | ANN_ICON_ONLY = "仅显示图标而不是按钮标签"
70 | ANN_ITEM_ADD_NEW = "添加新组或工具"
71 | ANN_ITEM_DELETE_EXISTING = "删除现有组或按钮"
72 | ANN_ITEM_DISPLAY_COMMAND = "在文本编辑器中显示按钮命令"
73 | ANN_ITEM_IMPORT = "从配置文件导入组或工具"
74 | ANN_ITEM_OVERWRITE = ("覆盖现有按钮的命令。\n"
75 | "使用星号*保留现有设置")
76 | ANN_ITEM_REORDER = "重新排序组和按钮"
77 | ANN_MODE = "切换编辑模式"
78 | ANN_NAME = ("组或工具的名称。\n"
79 | "它需要在所有组中以小写形式是唯一的")
80 | ANN_NEW_GROUP = "添加新组而不是新工具"
81 | ANN_NEW_SET = "添加新工具集"
82 | ANN_PROPERTY = "向工具添加数值、布尔或字符串属性"
83 | ANN_PROPERTY_NAME = "属性名称"
84 | ANN_PROPERTY_VALUE = "属性的默认值。要定义最小和最大值,请使用以下格式:值,值,值"
85 | ANN_PROPERTY_CALLBACK = "将命令字符串用作所有属性的更新选项的回调函数的主体"
86 | ANN_SELECT_GROUP = "选择一个组"
87 | ANN_SET_COLUMNS = "一行中的按钮数"
88 | ANN_SET_NAME = "按钮集的名称"
89 | ANN_TOGGLE_ADDON = "创建按钮以启用或禁用插件"
90 | ANN_TOOL = "要编辑的工具命令"
91 | ANN_TOOLTIP = "按钮的工具提示"
92 |
93 | # ----------------------------------------------------------------------
94 | # Info
95 | # ----------------------------------------------------------------------
96 |
97 |
98 | # ----------------------------------------------------------------------
99 | # Warning/Error
100 | # ----------------------------------------------------------------------
101 |
102 | WARNING_ADDON_INCOMPATIBLE = "插件不兼容,必须在首选项中启用/禁用:"
103 | WARNING_ADDON_MISSING = "不存在该名称的插件:"
104 | WARNING_BRACKETS_INCOMPLETE = "属性的括号不完整"
105 | WARNING_GROUP_NAME_EXISTS = "组名已存在"
106 | WARNING_IMAGE_MISSING = "路径中不存在该图像:"
107 | WARNING_LABEL_COMMAND_MISMATCH = "工具标签和命令的数量不匹配"
108 | WARNING_NO_GROUP_SELECTED = "未选择任何组"
109 | WARNING_NO_NAME = "未定义名称"
110 | WARNING_NO_TEXT_EDITOR = "没有打开的文本编辑器以获取工具命令"
111 | WARNING_NO_TEXT_EDITOR_TO_VIEW = "没有打开的文本编辑器以查看命令"
112 | WARNING_NO_TOOL_IN_GROUP = "在选定的组中不存在工具"
113 | WARNING_NO_TOOL_SELECTED = "未选择工具"
114 | WARNING_PROPERTY_NAME_MISSING = "属性名称和/或默认值丢失"
115 | WARNING_PROPERTY_VALUE_MISMATCH = "属性和值的数量不匹配"
116 | WARNING_SELECT_CONFIG = "选择有效的配置文件"
117 | WARNING_SELECT_TO_IMPORT = "选择要导入的组或工具"
118 | WARNING_TOOL_EXISTS_IN_GROUP = "该组中已存在工具名称"
119 |
--------------------------------------------------------------------------------
/toolShelf/preferences.py:
--------------------------------------------------------------------------------
1 | #
2 |
3 | from . import constants as const
4 | from . import config, language
5 |
6 | import bpy
7 |
8 | import importlib
9 |
10 |
11 | data = config.readConfig()
12 | if "language" in data:
13 | language.LANGUAGE = data["language"]
14 |
15 |
16 | # Get the current language.
17 | strings = language.getLanguage()
18 |
19 |
20 | # ----------------------------------------------------------------------
21 | # Preferences
22 | # ----------------------------------------------------------------------
23 |
24 | def getPreferences():
25 | """Return the preferences of the add-on.
26 |
27 | :return: The add-on preferences.
28 | :rtype: bpy.types.AddonPreferences
29 | """
30 | prefs = bpy.context.preferences.addons[const.NAME].preferences
31 | return prefs
32 |
33 |
34 | def updateLanguageCallback(self, context):
35 | """Property callback for changing the active language.
36 |
37 | :param context: The current context.
38 | :type context: bpy.context
39 | """
40 | # Save the new language setting to the configuration.
41 | data = config.readConfig()
42 | data["language"] = getPreferences().language
43 | config.backupConfig(data)
44 | config.jsonWrite(config.CONFIG_PATH, data)
45 |
46 | # language.reloadDependencies()
47 |
48 |
49 | def reload():
50 | """Reload the dependencies and the add-on.
51 |
52 | Also used for updating the panel when adding, editing or removing
53 | groups or tools.
54 | """
55 | reloadDependencies()
56 | # Reload the add-on.
57 | bpy.ops.script.reload()
58 |
59 |
60 | def reloadDependencies():
61 | """Reload the main module to update the panels.
62 | """
63 | mods = ["toolShelf"]
64 | for mod in mods:
65 | moduleName = ".".join([const.NAME, mod])
66 | try:
67 | module = __import__(moduleName, fromlist=[""])
68 | importlib.reload(module)
69 |
70 | except (Exception, ):
71 | pass
72 |
73 |
74 | class TOOLSHELFPreferences(bpy.types.AddonPreferences):
75 | """Create the layout for the preferences.
76 | """
77 | bl_idname = const.NAME
78 |
79 | language: bpy.props.EnumProperty(name=strings.LANGUAGE_LABEL,
80 | items=language.LANGUAGE_ITEMS,
81 | description=strings.ANN_LANGUAGE,
82 | default=const.LANGUAGE,
83 | update=updateLanguageCallback)
84 |
85 | def draw(self, context):
86 | """Draw the panel and it's properties.
87 |
88 | :param context: The current context.
89 | :type context: bpy.context
90 | """
91 | self.layout.use_property_split = True
92 |
93 | col = self.layout.column(align=True)
94 | col.prop(self, "language")
95 |
96 |
97 | # ----------------------------------------------------------------------
98 | # Registration
99 | # ----------------------------------------------------------------------
100 |
101 | # Collect all classes in a list for easier access.
102 | classes = [TOOLSHELFPreferences]
103 |
104 |
105 | def register():
106 | """Register the add-on.
107 | """
108 | for cls in classes:
109 | bpy.utils.register_class(cls)
110 |
111 |
112 | def unregister():
113 | """Unregister the add-on.
114 | """
115 | for cls in classes:
116 | bpy.utils.unregister_class(cls)
117 |
--------------------------------------------------------------------------------