Skip to content

Instantly share code, notes, and snippets.

@p2or
Last active May 21, 2023 18:12
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save p2or/ab314836285579cdfdb5d7848af8ee5c to your computer and use it in GitHub Desktop.
Save p2or/ab314836285579cdfdb5d7848af8ee5c to your computer and use it in GitHub Desktop.
bl_info = {
"name": "Mesh Islands Random Vertex Color",
"author": "batFINGER, p2or",
"version": (1, 0),
"blender": (2, 80, 0),
"location": "F3 Search or Vertex Paint > Paint > Random Vertex Colors",
"description": "Assign random vertex color to mesh islands",
"warning": "",
"wiki_url": "",
"category": "Mesh"}
import bpy
import bmesh
from random import uniform, sample
from bpy.props import StringProperty, BoolProperty, FloatVectorProperty
from mathutils import Vector
from colorsys import hsv_to_rgb
def walk_island(vert):
''' walk all un-tagged linked verts '''
vert.tag = True
yield (vert)
linked_verts = [
e.other_vert(vert) for e in vert.link_edges
if not e.other_vert(vert).tag]
for v in linked_verts:
if v.tag:
continue
yield from walk_island(v)
def get_islands(bm, verts=[]):
def tag(verts, switch):
for v in verts:
v.tag = switch
tag(bm.verts, True)
tag(verts, False)
ret = {"islands": []}
verts = set(verts)
while verts:
v = verts.pop()
verts.add(v)
island = set(walk_island(v))
faces = set(
f for x in island for f in x.link_faces
if all(v.tag for v in f.verts))
edges = set(
e for x in island for e in x.link_edges
if all(v.tag for v in e.verts))
ret["islands"].append(island.union(edges).union(faces))
tag(island, False) # remove tag = True
verts -= island
return ret
class BMIslands(list):
def __init__(self, bm):
self.extend(BMeshIsland(i, island) for i, island in enumerate(get_islands(bm, verts=bm.verts)["islands"]))
self.sort(key=lambda i: (i.co.x, i.co.y))
for i, island in enumerate(self):
island.index = i
class BMeshIsland:
def __init__(self, index, geom):
self.index = index
self.verts = [e for e in geom if isinstance(e, bmesh.types.BMVert)]
self.co = sum([v.co for v in self.verts], Vector()) / len(self.verts)
self.faces = [e for e in geom if isinstance(e, bmesh.types.BMFace)]
class MESH_OT_random_island_vertex_color(bpy.types.Operator):
"""Generate random Vertex Colors per Island"""
bl_idname = "mesh.random_island_vert_color"
bl_label = "Random Color per Vertex Island"
bl_options = {'REGISTER', 'UNDO'}
islands = None
bm = None
layer: StringProperty(
name="Layer", default="Random")
satuation: FloatVectorProperty(
size=2, default=(0.6,1.0), max=1.0, min=0.0)
lightness: FloatVectorProperty(
size=2, default=(0.6,1.0), max=1.0, min=0.0)
shuffle: BoolProperty(
name="Shuffle", default=True)
@classmethod
def poll(cls, context):
return(context.object
and context.object.mode == 'OBJECT'
or context.object.mode == 'VERTEX_PAINT'
and context.object.type == 'MESH')
def main(self, ob):
me = ob.data
bm = bmesh.new()
bm.from_mesh(me)
self.bm = bm
self.islands = BMIslands(bm)
def random_color(self, alpha=1):
return (uniform(0, 1), uniform(0, 1), uniform(0, 1), alpha)
def random_hsv_colors(self, size, sat_limits, val_limits, shuffle=True, alpha=1):
rgb_clrs = {}
for i in range(size):
h, s, v = (
(i*(1/size)),
uniform(sat_limits[0], sat_limits[1]),
uniform(val_limits[0], val_limits[1]))
r, g, b = hsv_to_rgb(h, s, v)
rgb_clrs[i] = (r, g, b, alpha)
if shuffle:
seed = sample(list(rgb_clrs.values()), len(rgb_clrs))
rgb_clrs = {i : seed[i] for i in range(len(seed))}
return rgb_clrs
def randomize(self, ob):
me = ob.data
clayers = self.bm.loops.layers.color
color_layer = clayers.get(self.layer) or clayers.new(self.layer)
clrs = self.random_hsv_colors(
len(self.islands), self.satuation, self.lightness, self.shuffle)
ami = ob.active_material_index
for c, island in enumerate(self.islands):
rbga = clrs[c]
for f in island.faces:
f.material_index = ami
for l in f.loops:
l[color_layer] = rbga
self.bm.to_mesh(me)
me.update()
me.vertex_colors.active = me.vertex_colors[self.layer]
def invoke(self, context, event):
ob = context.object
self.main(ob)
return self.execute(context)
def execute(self, context):
self.randomize(context.object)
return {'FINISHED'}
def draw(self, context):
layout = self.layout
row = layout.row()
layout.prop(self, "satuation", text="Saturation (Min Max)")
layout.prop(self, "lightness", text="Lightness (Min Max)")
layout.separator()
layout.prop(self, "shuffle", text="Shuffle Colors")
layout.separator()
layout.prop(self, "layer")
layout.separator()
layout.operator(self.__class__.bl_idname, text="Randomize", icon="FILE_REFRESH")
def draw_random_island_vertex_color(self, context):
layout = self.layout
layout.operator(
MESH_OT_random_island_vertex_color.bl_idname,
icon='RESTRICT_COLOR_ON',
text="Random Vertex Colors")
def register():
bpy.utils.register_class(MESH_OT_random_island_vertex_color)
bpy.types.VIEW3D_MT_paint_vertex.append(draw_random_island_vertex_color)
def unregister():
bpy.utils.unregister_class(MESH_OT_random_island_vertex_color)
bpy.types.VIEW3D_MT_paint_vertex.remove(draw_random_island_vertex_color)
if __name__ == "__main__":
register()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment