1 Star 0 Fork 0

boss/blender-addons

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
文件
该仓库未声明开源许可证文件(LICENSE),使用请关注具体项目描述及其代码上游依赖。
克隆/下载
object_color_rules.py 14.25 KB
一键复制 编辑 原始数据 按行查看 历史
Aaron Carlisle 提交于 2020-03-05 16:26 . Addons: Use Manual URL prefix
# ***** BEGIN GPL LICENSE BLOCK *****
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software Foundation,
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# ***** END GPL LICENCE BLOCK *****
bl_info = {
"name": "Object Color Rules",
"author": "Campbell Barton",
"version": (0, 0, 2),
"blender": (2, 80, 0),
"location": "Properties > Object Buttons",
"description": "Rules for assigning object color (for object & wireframe colors).",
"doc_url": "{BLENDER_MANUAL_URL}/addons/object/color_rules.html",
"category": "Object",
}
def test_name(rule, needle, haystack, cache):
if rule.use_match_regex:
if not cache:
import re
re_needle = re.compile(needle)
cache[:] = [re_needle]
else:
re_needle = cache[0]
return (re_needle.match(haystack) is not None)
else:
return (needle in haystack)
class rule_test:
__slots__ = ()
def __new__(cls, *args, **kwargs):
raise RuntimeError("%s should not be instantiated" % cls)
@staticmethod
def NAME(obj, rule, cache):
match_name = rule.match_name
return test_name(rule, match_name, obj.name, cache)
def DATA(obj, rule, cache):
match_name = rule.match_name
obj_data = obj.data
if obj_data is not None:
return test_name(rule, match_name, obj_data.name, cache)
else:
return False
@staticmethod
def COLLECTION(obj, rule, cache):
if not cache:
match_name = rule.match_name
objects = {o for g in bpy.data.collections if test_name(rule, match_name, g.name, cache) for o in g.objects}
cache["objects"] = objects
else:
objects = cache["objects"]
return obj in objects
@staticmethod
def MATERIAL(obj, rule, cache):
match_name = rule.match_name
materials = getattr(obj.data, "materials", None)
return ((materials is not None) and
(any((test_name(rule, match_name, m.name) for m in materials if m is not None))))
@staticmethod
def TYPE(obj, rule, cache):
return (obj.type == rule.match_object_type)
@staticmethod
def EXPR(obj, rule, cache):
if not cache:
match_expr = rule.match_expr
expr = compile(match_expr, rule.name, 'eval')
namespace = {}
namespace.update(__import__("math").__dict__)
cache["expr"] = expr
cache["namespace"] = namespace
else:
expr = cache["expr"]
namespace = cache["namespace"]
try:
return bool(eval(expr, {}, {"self": obj}))
except:
import traceback
traceback.print_exc()
return False
class rule_draw:
__slots__ = ()
def __new__(cls, *args, **kwargs):
raise RuntimeError("%s should not be instantiated" % cls)
@staticmethod
def _generic_match_name(layout, rule):
layout.label(text="Match Name:")
row = layout.row(align=True)
row.prop(rule, "match_name", text="")
row.prop(rule, "use_match_regex", text="", icon='SORTALPHA')
@staticmethod
def NAME(layout, rule):
rule_draw._generic_match_name(layout, rule)
@staticmethod
def DATA(layout, rule):
rule_draw._generic_match_name(layout, rule)
@staticmethod
def COLLECTION(layout, rule):
rule_draw._generic_match_name(layout, rule)
@staticmethod
def MATERIAL(layout, rule):
rule_draw._generic_match_name(layout, rule)
@staticmethod
def TYPE(layout, rule):
row = layout.row()
row.prop(rule, "match_object_type")
@staticmethod
def EXPR(layout, rule):
col = layout.column()
col.label(text="Scripted Expression:")
col.prop(rule, "match_expr", text="")
def object_colors_calc(rules, objects):
from mathutils import Color
rules_cb = [getattr(rule_test, rule.type) for rule in rules]
rules_blend = [(1.0 - rule.factor, rule.factor) for rule in rules]
rules_color = [Color(rule.color) for rule in rules]
rules_cache = [{} for i in range(len(rules))]
rules_inv = [rule.use_invert for rule in rules]
changed_count = 0
for obj in objects:
is_set = False
obj_color = Color(obj.color[0:3])
for (rule, test_cb, color, blend, cache, use_invert) \
in zip(rules, rules_cb, rules_color, rules_blend, rules_cache, rules_inv):
if test_cb(obj, rule, cache) is not use_invert:
if is_set is False:
obj_color = color
else:
# prevent mixing colors losing saturation
obj_color_s = obj_color.s
obj_color = (obj_color * blend[0]) + (color * blend[1])
obj_color.s = (obj_color_s * blend[0]) + (color.s * blend[1])
is_set = True
if is_set:
obj.color[0:3] = obj_color
changed_count += 1
return changed_count
def object_colors_select(rule, objects):
cache = {}
rule_type = rule.type
test_cb = getattr(rule_test, rule_type)
for obj in objects:
obj.select = test_cb(obj, rule, cache)
def object_colors_rule_validate(rule, report):
rule_type = rule.type
if rule_type in {'NAME', 'DATA', 'COLLECTION', 'MATERIAL'}:
if rule.use_match_regex:
import re
try:
re.compile(rule.match_name)
except Exception as e:
report({'ERROR'}, "Rule %r: %s" % (rule.name, str(e)))
return False
elif rule_type == 'EXPR':
try:
compile(rule.match_expr, rule.name, 'eval')
except Exception as e:
report({'ERROR'}, "Rule %r: %s" % (rule.name, str(e)))
return False
return True
import bpy
from bpy.types import (
Operator,
Panel,
UIList,
)
from bpy.props import (
StringProperty,
BoolProperty,
IntProperty,
FloatProperty,
EnumProperty,
CollectionProperty,
BoolVectorProperty,
FloatVectorProperty,
)
class OBJECT_PT_color_rules(Panel):
bl_label = "Color Rules"
bl_space_type = 'PROPERTIES'
bl_region_type = 'WINDOW'
bl_context = "object"
bl_options = {'DEFAULT_CLOSED'}
def draw(self, context):
layout = self.layout
scene = context.scene
# Rig type list
row = layout.row()
row.template_list(
"OBJECT_UL_color_rule", "color_rules",
scene, "color_rules",
scene, "color_rules_active_index",
)
col = row.column()
colsub = col.column(align=True)
colsub.operator("object.color_rules_add", icon='ADD', text="")
colsub.operator("object.color_rules_remove", icon='REMOVE', text="")
colsub = col.column(align=True)
colsub.operator("object.color_rules_move", text="", icon='TRIA_UP').direction = -1
colsub.operator("object.color_rules_move", text="", icon='TRIA_DOWN').direction = 1
colsub = col.column(align=True)
colsub.operator("object.color_rules_select", text="", icon='RESTRICT_SELECT_OFF')
if scene.color_rules:
index = scene.color_rules_active_index
rule = scene.color_rules[index]
box = layout.box()
row = box.row(align=True)
row.prop(rule, "name", text="")
row.prop(rule, "type", text="")
row.prop(rule, "use_invert", text="", icon='ARROW_LEFTRIGHT')
draw_cb = getattr(rule_draw, rule.type)
draw_cb(box, rule)
row = layout.split(factor=0.75, align=True)
props = row.operator("object.color_rules_assign", text="Assign Selected")
props.use_selection = True
props = row.operator("object.color_rules_assign", text="All")
props.use_selection = False
class OBJECT_UL_color_rule(UIList):
def draw_item(self, context, layout, data, rule, icon, active_data, active_propname, index):
# assert(isinstance(rule, bpy.types.ShapeKey))
# scene = active_data
split = layout.split(factor=0.5)
row = split.split(align=False)
row.label(text="%s (%s)" % (rule.name, rule.type.lower()))
split = split.split(factor=0.7)
split.prop(rule, "factor", text="", emboss=False)
split.prop(rule, "color", text="")
class OBJECT_OT_color_rules_assign(Operator):
"""Assign colors to objects based on user rules"""
bl_idname = "object.color_rules_assign"
bl_label = "Assign Colors"
bl_options = {'UNDO'}
use_selection: BoolProperty(
name="Selected",
description="Apply to selected (otherwise all objects in the scene)",
default=True,
)
def execute(self, context):
scene = context.scene
if self.use_selection:
objects = context.selected_editable_objects
else:
objects = scene.objects
rules = scene.color_rules[:]
for rule in rules:
if not object_colors_rule_validate(rule, self.report):
return {'CANCELLED'}
changed_count = object_colors_calc(rules, objects)
self.report({'INFO'}, "Set colors for {} of {} objects".format(changed_count, len(objects)))
return {'FINISHED'}
class OBJECT_OT_color_rules_select(Operator):
"""Select objects matching the current rule"""
bl_idname = "object.color_rules_select"
bl_label = "Select Rule"
bl_options = {'UNDO'}
def execute(self, context):
scene = context.scene
rule = scene.color_rules[scene.color_rules_active_index]
if not object_colors_rule_validate(rule, self.report):
return {'CANCELLED'}
objects = context.visible_objects
object_colors_select(rule, objects)
return {'FINISHED'}
class OBJECT_OT_color_rules_add(Operator):
bl_idname = "object.color_rules_add"
bl_label = "Add Color Layer"
bl_options = {'UNDO'}
def execute(self, context):
scene = context.scene
rules = scene.color_rules
rule = rules.add()
rule.name = "Rule.%.3d" % len(rules)
scene.color_rules_active_index = len(rules) - 1
return {'FINISHED'}
class OBJECT_OT_color_rules_remove(Operator):
bl_idname = "object.color_rules_remove"
bl_label = "Remove Color Layer"
bl_options = {'UNDO'}
def execute(self, context):
scene = context.scene
rules = scene.color_rules
rules.remove(scene.color_rules_active_index)
if scene.color_rules_active_index > len(rules) - 1:
scene.color_rules_active_index = len(rules) - 1
return {'FINISHED'}
class OBJECT_OT_color_rules_move(Operator):
bl_idname = "object.color_rules_move"
bl_label = "Remove Color Layer"
bl_options = {'UNDO'}
direction: IntProperty()
def execute(self, context):
scene = context.scene
rules = scene.color_rules
index = scene.color_rules_active_index
index_new = index + self.direction
if index_new < len(rules) and index_new >= 0:
rules.move(index, index_new)
scene.color_rules_active_index = index_new
return {'FINISHED'}
else:
return {'CANCELLED'}
class ColorRule(bpy.types.PropertyGroup):
name: StringProperty(
name="Rule Name",
)
color: FloatVectorProperty(
name="Color",
description="Color to assign",
subtype='COLOR', size=3, min=0, max=1, precision=3, step=0.1,
default=(0.5, 0.5, 0.5),
)
factor: FloatProperty(
name="Opacity",
description="Color to assign",
min=0, max=1, precision=1, step=0.1,
default=1.0,
)
type: EnumProperty(
name="Rule Type",
items=(
('NAME', "Name", "Object name contains this text (or matches regex)"),
('DATA', "Data Name", "Object data name contains this text (or matches regex)"),
('COLLECTION', "Collection Name", "Object in collection that contains this text (or matches regex)"),
('MATERIAL', "Material Name", "Object uses a material name that contains this text (or matches regex)"),
('TYPE', "Type", "Object type"),
('EXPR', "Expression", (
"Scripted expression (using 'self' for the object) eg:\n"
" self.type == 'MESH' and len(self.data.vertices) > 20"
)
),
),
)
use_invert: BoolProperty(
name="Invert",
description="Match when the rule isn't met",
)
# ------------------
# Matching Variables
# shared by all name matching
match_name: StringProperty(
name="Match Name",
)
use_match_regex: BoolProperty(
name="Regex",
description="Use regular expressions for pattern matching",
)
# type == 'TYPE'
match_object_type: EnumProperty(
name="Object Type",
items=([(i.identifier, i.name, "")
for i in bpy.types.Object.bl_rna.properties['type'].enum_items]
)
)
# type == 'EXPR'
match_expr: StringProperty(
name="Expression",
description="Python expression, where 'self' is the object variable"
)
classes = (
OBJECT_PT_color_rules,
OBJECT_OT_color_rules_add,
OBJECT_OT_color_rules_remove,
OBJECT_OT_color_rules_move,
OBJECT_OT_color_rules_assign,
OBJECT_OT_color_rules_select,
OBJECT_UL_color_rule,
ColorRule,
)
def register():
for cls in classes:
bpy.utils.register_class(cls)
bpy.types.Scene.color_rules = CollectionProperty(type=ColorRule)
bpy.types.Scene.color_rules_active_index = IntProperty()
def unregister():
for cls in classes:
bpy.utils.unregister_class(cls)
del bpy.types.Scene.color_rules
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
1
https://gitee.com/bosspoi/blender-addons.git
git@gitee.com:bosspoi/blender-addons.git
bosspoi
blender-addons
blender-addons
asset-browser-poselib

搜索帮助