Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender-addons.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'mesh_tissue/colors_groups_exchanger.py')
-rw-r--r--mesh_tissue/colors_groups_exchanger.py2468
1 files changed, 0 insertions, 2468 deletions
diff --git a/mesh_tissue/colors_groups_exchanger.py b/mesh_tissue/colors_groups_exchanger.py
deleted file mode 100644
index 98427477..00000000
--- a/mesh_tissue/colors_groups_exchanger.py
+++ /dev/null
@@ -1,2468 +0,0 @@
-# SPDX-License-Identifier: GPL-2.0-or-later
-
-#-------------------------- COLORS / GROUPS EXCHANGER -------------------------#
-# #
-# Vertex Color to Vertex Group allow you to convert colors channels to weight #
-# maps. #
-# The main purpose is to use vertex colors to store information when importing #
-# files from other software. The script works with the active vertex color #
-# slot. #
-# For use the command "Vertex Clors to Vertex Groups" use the search bar #
-# (space bar). #
-# #
-# (c) Alessandro Zomparelli #
-# (2017) #
-# #
-# http://www.co-de-it.com/ #
-# #
-################################################################################
-
-import bpy, bmesh
-import numpy as np
-import math, timeit, time
-from math import *#pi, sin
-from statistics import mean, stdev
-from mathutils import Vector
-from numpy import *
-try: from .numba_functions import numba_reaction_diffusion
-except: pass
-
-from bpy.types import (
- Operator,
- Panel,
- PropertyGroup,
- )
-
-from bpy.props import (
- BoolProperty,
- EnumProperty,
- FloatProperty,
- IntProperty,
- StringProperty,
- FloatVectorProperty,
- IntVectorProperty
-)
-
-from .utils import *
-
-def reaction_diffusion_add_handler(self, context):
- # remove existing handlers
- old_handlers = []
- for h in bpy.app.handlers.frame_change_post:
- if "reaction_diffusion" in str(h):
- old_handlers.append(h)
- for h in old_handlers: bpy.app.handlers.frame_change_post.remove(h)
- # add new handler
- bpy.app.handlers.frame_change_post.append(reaction_diffusion_def)
-
-class formula_prop(PropertyGroup):
- name : StringProperty()
- formula : StringProperty()
- float_var : FloatVectorProperty(name="", description="", default=(0, 0, 0, 0, 0), size=5)
- int_var : IntVectorProperty(name="", description="", default=(0, 0, 0, 0, 0), size=5)
-
-class reaction_diffusion_prop(PropertyGroup):
- run : BoolProperty(default=False, update = reaction_diffusion_add_handler,
- description='Compute a new iteration on frame changes. Currently is not working during Render Animation')
-
- time_steps : bpy.props.IntProperty(
- name="Steps", default=10, min=0, soft_max=50,
- description="Number of Steps")
-
- dt : bpy.props.FloatProperty(
- name="dt", default=1, min=0, soft_max=0.2,
- description="Time Step")
-
- diff_a : bpy.props.FloatProperty(
- name="Diff A", default=0.1, min=0, soft_max=2, precision=3,
- description="Diffusion A")
-
- diff_b : bpy.props.FloatProperty(
- name="Diff B", default=0.05, min=0, soft_max=2, precision=3,
- description="Diffusion B")
-
- f : bpy.props.FloatProperty(
- name="f", default=0.055, min=0, soft_max=0.5, precision=3,
- description="Feed Rate")
-
- k : bpy.props.FloatProperty(
- name="k", default=0.062, min=0, soft_max=0.5, precision=3,
- description="Kill Rate")
-
- diff_mult : bpy.props.FloatProperty(
- name="Scale", default=1, min=0, soft_max=1, max=2, precision=2,
- description="Multiplier for the diffusion of both substances")
-
-def compute_formula(ob=None, formula="rx", float_var=(0,0,0,0,0), int_var=(0,0,0,0,0)):
- verts = ob.data.vertices
- n_verts = len(verts)
-
- f1,f2,f3,f4,f5 = float_var
- i1,i2,i3,i4,i5 = int_var
-
- do_groups = "w[" in formula
- do_local = "lx" in formula or "ly" in formula or "lz" in formula
- do_global = "gx" in formula or "gy" in formula or "gz" in formula
- do_relative = "rx" in formula or "ry" in formula or "rz" in formula
- do_normal = "nx" in formula or "ny" in formula or "nz" in formula
- mat = ob.matrix_world
-
- for i in range(1000):
- if "w["+str(i)+"]" in formula and i > len(ob.vertex_groups)-1:
- return "w["+str(i)+"] not found"
-
- w = []
- for i in range(len(ob.vertex_groups)):
- w.append([])
- if "w["+str(i)+"]" in formula:
- vg = ob.vertex_groups[i]
- for v in verts:
- try:
- w[i].append(vg.weight(v.index))
- except:
- w[i].append(0)
- w[i] = array(w[i])
-
- start_time = timeit.default_timer()
- # compute vertex coordinates
- if do_local or do_relative or do_global:
- co = [0]*n_verts*3
- verts.foreach_get('co', co)
- np_co = array(co).reshape((n_verts, 3))
- lx, ly, lz = array(np_co).transpose()
- if do_relative:
- rx = np.interp(lx, (lx.min(), lx.max()), (0, +1))
- ry = np.interp(ly, (ly.min(), ly.max()), (0, +1))
- rz = np.interp(lz, (lz.min(), lz.max()), (0, +1))
- if do_global:
- co = [v.co for v in verts]
- global_co = []
- for v in co:
- global_co.append(mat * v)
- global_co = array(global_co).reshape((n_verts, 3))
- gx, gy, gz = array(global_co).transpose()
- # compute vertex normals
- if do_normal:
- normal = [0]*n_verts*3
- verts.foreach_get('normal', normal)
- normal = array(normal).reshape((n_verts, 3))
- nx, ny, nz = array(normal).transpose()
-
- try:
- weight = eval(formula)
- return weight
- except:
- return "There is something wrong"
- print("Weight Formula: " + str(timeit.default_timer() - start_time))
-
-class weight_formula_wiki(bpy.types.Operator):
- bl_idname = "scene.weight_formula_wiki"
- bl_label = "Online Documentation"
- bl_options = {'REGISTER', 'UNDO'}
-
- def execute(self, context):
- bpy.ops.wm.url_open(url="https://github.com/alessandro-zomparelli/tissue/wiki/Weight-Tools#weight-formula")
- return {'FINISHED'}
-
-class weight_formula(bpy.types.Operator):
- bl_idname = "object.weight_formula"
- bl_label = "Weight Formula"
- bl_options = {'REGISTER', 'UNDO'}
-
- ex = [
- #'cos(arctan(nx/ny)*6 + sin(rz*30)*0.5)/2 + cos(arctan(nx/ny)*6 - sin(rz*30)*0.5 + pi/2)/2 + 0.5',
- 'cos(arctan(nx/ny)*i1*2 + sin(rz*i3))/i2 + cos(arctan(nx/ny)*i1*2 - sin(rz*i3))/i2 + 0.5',
- 'cos(arctan(nx/ny)*i1*2 + sin(rz*i2))/2 + cos(arctan(nx/ny)*i1*2 - sin(rz*i2))/2',
- '(sin(arctan(nx/ny)*i1)*sin(nz*i1)+1)/2',
- 'cos(arctan(nx/ny)*f1)',
- 'cos(arctan(lx/ly)*f1 + sin(rz*f2)*f3)',
- 'sin(nx*15)<sin(ny*15)',
- 'cos(ny*rz**2*i1)',
- 'sin(rx*30) > 0',
- 'sin(nz*i1)',
- 'w[0]**2',
- 'sqrt((rx-0.5)**2 + (ry-0.5)**2)*2',
- 'abs(0.5-rz)*2',
- 'rx'
- ]
- ex_items = list((s,s,"") for s in ex)
- ex_items.append(('CUSTOM', "User Formula", ""))
-
- examples : bpy.props.EnumProperty(
- items = ex_items, default='CUSTOM', name="Examples")
-
- old_ex = ""
-
- formula : bpy.props.StringProperty(
- name="Formula", default="", description="Formula to Evaluate")
- bl_description = ("Generate a Vertex Group based on the given formula")
-
- slider_f01 : bpy.props.FloatProperty(
- name="f1", default=1, description="Slider")
- bl_description = ("Slider Float 1")
- slider_f02 : bpy.props.FloatProperty(
- name="f2", default=1, description="Slider")
- bl_description = ("Slider Float 2")
- slider_f03 : bpy.props.FloatProperty(
- name="f3", default=1, description="Slider")
- bl_description = ("Slider Float 3")
- slider_f04 : bpy.props.FloatProperty(
- name="f4", default=1, description="Slider")
- bl_description = ("Slider Float 4")
- slider_f05 : bpy.props.FloatProperty(
- name="f5", default=1, description="Slider")
- bl_description = ("Slider Float 5")
- slider_i01 : bpy.props.IntProperty(
- name="i1", default=1, description="Slider")
- bl_description = ("Slider Integer 1")
- slider_i02 : bpy.props.IntProperty(
- name="i2", default=1, description="Slider")
- bl_description = ("Slider Integer 2")
- slider_i03 : bpy.props.IntProperty(
- name="i3", default=1, description="Slider")
- bl_description = ("Slider Integer 3")
- slider_i04 : bpy.props.IntProperty(
- name="i4", default=1, description="Slider")
- bl_description = ("Slider Integer 4")
- slider_i05 : bpy.props.IntProperty(
- name="i5", default=1, description="Slider")
- bl_description = ("Slider Integer 5")
-
- def invoke(self, context, event):
- return context.window_manager.invoke_props_dialog(self, width=350)
-
- def draw(self, context):
- layout = self.layout
- #layout.label(text="Examples")
- layout.prop(self, "examples", text="Examples")
- #if self.examples == 'CUSTOM':
- layout.label(text="Formula")
- layout.prop(self, "formula", text="")
- #try: self.examples = self.formula
- #except: pass
-
- if self.examples != self.old_ex and self.examples != 'CUSTOM':
- self.formula = self.examples
- self.old_ex = self.examples
- elif self.formula != self.examples:
- self.examples = 'CUSTOM'
- formula = self.formula
-
- layout.separator()
- if "f1" in formula: layout.prop(self, "slider_f01")
- if "f2" in formula: layout.prop(self, "slider_f02")
- if "f3" in formula: layout.prop(self, "slider_f03")
- if "f4" in formula: layout.prop(self, "slider_f04")
- if "f5" in formula: layout.prop(self, "slider_f05")
- if "i1" in formula: layout.prop(self, "slider_i01")
- if "i2" in formula: layout.prop(self, "slider_i02")
- if "i3" in formula: layout.prop(self, "slider_i03")
- if "i4" in formula: layout.prop(self, "slider_i04")
- if "i5" in formula: layout.prop(self, "slider_i05")
-
- layout.label(text="Variables (for each vertex):")
- layout.label(text="lx, ly, lz: Local Coordinates", icon='ORIENTATION_LOCAL')
- layout.label(text="gx, gy, gz: Global Coordinates", icon='WORLD')
- layout.label(text="rx, ry, rz: Local Coordinates (0 to 1)", icon='NORMALIZE_FCURVES')
- layout.label(text="nx, ny, nz: Normal Coordinates", icon='SNAP_NORMAL')
- layout.label(text="w[0], w[1], w[2], ... : Vertex Groups", icon="GROUP_VERTEX")
- layout.separator()
- layout.label(text="f1, f2, f3, f4, f5: Float Sliders", icon='MOD_HUE_SATURATION')#PROPERTIES
- layout.label(text="i1, i2, i3, i4, i5: Integer Sliders", icon='MOD_HUE_SATURATION')
- layout.separator()
- #layout.label(text="All mathematical functions are based on Numpy", icon='INFO')
- #layout.label(text="https://docs.scipy.org/doc/numpy-1.13.0/reference/routines.math.html", icon='INFO')
- layout.operator("scene.weight_formula_wiki", icon="HELP")
- #layout.label(text="(where 'i' is the index of the Vertex Group)")
-
- def execute(self, context):
- ob = bpy.context.active_object
- n_verts = len(ob.data.vertices)
- #if self.examples == 'CUSTOM':
- # formula = self.formula
- #else:
- #self.formula = self.examples
- # formula = self.examples
-
- #f1, f2, f3, f4, f5 = self.slider_f01, self.slider_f02, self.slider_f03, self.slider_f04, self.slider_f05
- #i1, i2, i3, i4, i5 = self.slider_i01, self.slider_i02, self.slider_i03, self.slider_i04, self.slider_i05
- f_sliders = self.slider_f01, self.slider_f02, self.slider_f03, self.slider_f04, self.slider_f05
- i_sliders = self.slider_i01, self.slider_i02, self.slider_i03, self.slider_i04, self.slider_i05
-
- if self.examples != self.old_ex and self.examples != 'CUSTOM':
- self.formula = self.examples
- self.old_ex = self.examples
- elif self.formula != self.examples:
- self.examples = 'CUSTOM'
- formula = self.formula
-
- if formula == "": return {'FINISHED'}
- vertex_group_name = "Formula " + formula
- ob.vertex_groups.new(name=vertex_group_name)
-
- weight = compute_formula(ob, formula=formula, float_var=f_sliders, int_var=i_sliders)
- if type(weight) == str:
- self.report({'ERROR'}, weight)
- return {'CANCELLED'}
-
- #start_time = timeit.default_timer()
- weight = nan_to_num(weight)
- if type(weight) == int or type(weight) == float:
- for i in range(n_verts):
- ob.vertex_groups[-1].add([i], weight, 'REPLACE')
- elif type(weight) == ndarray:
- for i in range(n_verts):
- ob.vertex_groups[-1].add([i], weight[i], 'REPLACE')
- ob.data.update()
- bpy.ops.object.mode_set(mode='WEIGHT_PAINT')
-
- # Store formula settings
- new_formula = ob.formula_settings.add()
- new_formula.name = ob.vertex_groups[-1].name
- new_formula.formula = formula
- new_formula.int_var = i_sliders
- new_formula.float_var = f_sliders
-
- #for f in ob.formula_settings:
- # print(f.name, f.formula, f.int_var, f.float_var)
- return {'FINISHED'}
-
-class _weight_laplacian(bpy.types.Operator):
- bl_idname = "object._weight_laplacian"
- bl_label = "Weight Laplacian"
- bl_description = ("Compute the Vertex Group Laplacian")
- bl_options = {'REGISTER', 'UNDO'}
-
- bounds : bpy.props.EnumProperty(
- items=(('MANUAL', "Manual Bounds", ""),
- ('POSITIVE', "Positive Only", ""),
- ('NEGATIVE', "Negative Only", ""),
- ('AUTOMATIC', "Automatic Bounds", "")),
- default='AUTOMATIC', name="Bounds")
-
- mode : bpy.props.EnumProperty(
- items=(('LENGTH', "Length Weight", ""),
- ('SIMPLE', "Simple", "")),
- default='SIMPLE', name="Evaluation Mode")
-
- min_def : bpy.props.FloatProperty(
- name="Min", default=0, soft_min=-1, soft_max=0,
- description="Laplacian value with 0 weight")
-
- max_def : bpy.props.FloatProperty(
- name="Max", default=0.5, soft_min=0, soft_max=5,
- description="Laplacian value with 1 weight")
-
- bounds_string = ""
-
- frame = None
-
- @classmethod
- def poll(cls, context):
- return len(context.object.vertex_groups) > 0
-
- def draw(self, context):
- layout = self.layout
- col = layout.column(align=True)
- col.label(text="Evaluation Mode")
- col.prop(self, "mode", text="")
- col.label(text="Bounds")
- col.prop(self, "bounds", text="")
- if self.bounds == 'MANUAL':
- col.label(text="Strain Rate \u03B5:")
- col.prop(self, "min_def")
- col.prop(self, "max_def")
- col.label(text="\u03B5" + ": from " + self.bounds_string)
-
-
- def execute(self, context):
- try: ob = context.object
- except:
- self.report({'ERROR'}, "Please select an Object")
- return {'CANCELLED'}
-
- group_id = ob.vertex_groups.active_index
- input_group = ob.vertex_groups[group_id].name
-
- group_name = "Laplacian"
- ob.vertex_groups.new(name=group_name)
- me = ob.data
- bm = bmesh.new()
- bm.from_mesh(me)
- bm.edges.ensure_lookup_table()
-
- # store weight values
- weight = []
- for v in me.vertices:
- try:
- weight.append(ob.vertex_groups[input_group].weight(v.index))
- except:
- weight.append(0)
-
- n_verts = len(bm.verts)
- lap = [0]*n_verts
- for e in bm.edges:
- if self.mode == 'LENGTH':
- length = e.calc_length()
- if length == 0: continue
- id0 = e.verts[0].index
- id1 = e.verts[1].index
- lap[id0] += weight[id1]/length - weight[id0]/length
- lap[id1] += weight[id0]/length - weight[id1]/length
- else:
- id0 = e.verts[0].index
- id1 = e.verts[1].index
- lap[id0] += weight[id1] - weight[id0]
- lap[id1] += weight[id0] - weight[id1]
-
- mean_lap = mean(lap)
- stdev_lap = stdev(lap)
- filter_lap = [i for i in lap if mean_lap-2*stdev_lap < i < mean_lap+2*stdev_lap]
- if self.bounds == 'MANUAL':
- min_def = self.min_def
- max_def = self.max_def
- elif self.bounds == 'AUTOMATIC':
- min_def = min(filter_lap)
- max_def = max(filter_lap)
- self.min_def = min_def
- self.max_def = max_def
- elif self.bounds == 'NEGATIVE':
- min_def = 0
- max_def = min(filter_lap)
- self.min_def = min_def
- self.max_def = max_def
- elif self.bounds == 'POSITIVE':
- min_def = 0
- max_def = max(filter_lap)
- self.min_def = min_def
- self.max_def = max_def
- delta_def = max_def - min_def
-
- # check undeformed errors
- if delta_def == 0: delta_def = 0.0001
-
- for i in range(len(lap)):
- val = (lap[i]-min_def)/delta_def
- if val > 0.7: print(str(val) + " " + str(lap[i]))
- #val = weight[i] + 0.2*lap[i]
- ob.vertex_groups[-1].add([i], val, 'REPLACE')
- self.bounds_string = str(round(min_def,2)) + " to " + str(round(max_def,2))
- ob.vertex_groups[-1].name = group_name + " " + self.bounds_string
- ob.vertex_groups.update()
- ob.data.update()
- bpy.ops.object.mode_set(mode='WEIGHT_PAINT')
- return {'FINISHED'}
-
-class weight_laplacian(bpy.types.Operator):
- bl_idname = "object.weight_laplacian"
- bl_label = "Weight Laplacian"
- bl_description = ("Compute the Vertex Group Laplacian")
- bl_options = {'REGISTER', 'UNDO'}
-
- steps : bpy.props.IntProperty(
- name="Steps", default=10, min=0, soft_max=50,
- description="Number of Steps")
-
- dt : bpy.props.FloatProperty(
- name="dt", default=0.2, min=0, soft_max=0.2,
- description="Time Step")
-
- diff_a : bpy.props.FloatProperty(
- name="Diff A", default=1, min=0, soft_max=2,
- description="Diffusion A")
-
- diff_b : bpy.props.FloatProperty(
- name="Diff B", default=0.5, min=0, soft_max=2,
- description="Diffusion B")
-
- f : bpy.props.FloatProperty(
- name="f", default=0.055, min=0, soft_max=0.5,
- description="Feed Rate")
-
- k : bpy.props.FloatProperty(
- name="k", default=0.062, min=0, soft_max=0.5,
- description="Kill Rate")
-
- diff_mult : bpy.props.FloatProperty(
- name="Scale", default=1, min=0, soft_max=1, max=2, precision=2,
- description="Multiplier for the diffusion of both substances")
-
- bounds_string = ""
-
- frame = None
-
- @classmethod
- def poll(cls, context):
- return len(context.object.vertex_groups) > 0
-
-
- def execute(self, context):
- try: ob = context.object
- except:
- self.report({'ERROR'}, "Please select an Object")
- return {'CANCELLED'}
-
- me = ob.data
- bm = bmesh.new()
- bm.from_mesh(me)
- bm.edges.ensure_lookup_table()
-
- # store weight values
- a = []
- b = []
- for v in me.vertices:
- try:
- a.append(ob.vertex_groups["A"].weight(v.index))
- except:
- a.append(0)
- try:
- b.append(ob.vertex_groups["B"].weight(v.index))
- except:
- b.append(0)
-
- a = array(a)
- b = array(b)
- f = self.f
- k = self.k
- diff_a = self.diff_a * self.diff_mult
- diff_b = self.diff_b * self.diff_mult
- dt = self.dt
-
- # initialize
- n_verts = len(bm.verts)
- # find max number of edges for vertex
- max_edges = 0
- n_neighbors = []
- id_neighbors = []
- for v in bm.verts:
- n_edges = len(v.link_edges)
- max_edges = max(max_edges, n_edges)
- n_neighbors.append(n_edges)
- neighbors = []
- for e in link_edges:
- for v1 in e.verts:
- if v != v1: neighbors.append(v1.index)
- id_neighbors.append(neighbors)
- n_neighbors = array(n_neighbors)
-
-
- a = [[] for i in range(n_verts)]
- lap_map = []
-
- for e in bm.edges:
- id0 = e.verts[0].index
- id1 = e.verts[1].index
- lap_map[id0].append(id1)
- lap_map[id1].append(id0)
-
- e1 = array(e1)
- e2 = array(e2)
- lap_a = a[e1]
-
- for i in range(self.steps):
-
- lap_a = zeros((n_verts))#[0]*n_verts
- lap_b = zeros((n_verts))#[0]*n_verts
- for e in bm.edges:
- id0 = e.verts[0].index
- id1 = e.verts[1].index
- lap_a[id0] += a[id1] - a[id0]
- lap_a[id1] += a[id0] - a[id1]
- lap_b[id0] += b[id1] - b[id0]
- lap_b[id1] += b[id0] - b[id1]
- ab2 = a*b**2
- a += (diff_a*lap_a - ab2 + f*(1-a))*dt
- b += (diff_b*lap_b + ab2 - (k+f)*b)*dt
-
- for i in range(n_verts):
- ob.vertex_groups['A'].add([i], a[i], 'REPLACE')
- ob.vertex_groups['B'].add([i], b[i], 'REPLACE')
- ob.vertex_groups.update()
- ob.data.update()
- bpy.ops.object.mode_set(mode='WEIGHT_PAINT')
- return {'FINISHED'}
-
-
-class reaction_diffusion(bpy.types.Operator):
- bl_idname = "object.reaction_diffusion"
- bl_label = "Reaction Diffusion"
- bl_description = ("Run a Reaction-Diffusion based on existing Vertex Groups: A and B")
- bl_options = {'REGISTER', 'UNDO'}
-
- steps : bpy.props.IntProperty(
- name="Steps", default=10, min=0, soft_max=50,
- description="Number of Steps")
-
- dt : bpy.props.FloatProperty(
- name="dt", default=0.2, min=0, soft_max=0.2,
- description="Time Step")
-
- diff_a : bpy.props.FloatProperty(
- name="Diff A", default=1, min=0, soft_max=2,
- description="Diffusion A")
-
- diff_b : bpy.props.FloatProperty(
- name="Diff B", default=0.5, min=0, soft_max=2,
- description="Diffusion B")
-
- f : bpy.props.FloatProperty(
- name="f", default=0.055, min=0, soft_max=0.5,
- description="Feed Rate")
-
- k : bpy.props.FloatProperty(
- name="k", default=0.062, min=0, soft_max=0.5,
- description="Kill Rate")
-
- bounds_string = ""
-
- frame = None
-
- @classmethod
- def poll(cls, context):
- return len(context.object.vertex_groups) > 0
-
-
- def execute(self, context):
- #bpy.app.handlers.frame_change_post.remove(reaction_diffusion_def)
- reaction_diffusion_add_handler(self, context)
- set_animatable_fix_handler(self, context)
- try: ob = context.object
- except:
- self.report({'ERROR'}, "Please select an Object")
- return {'CANCELLED'}
-
- me = ob.data
- bm = bmesh.new()
- bm.from_mesh(me)
- bm.edges.ensure_lookup_table()
-
- # store weight values
- a = []
- b = []
- for v in me.vertices:
- try:
- a.append(ob.vertex_groups["A"].weight(v.index))
- except:
- a.append(0)
- try:
- b.append(ob.vertex_groups["B"].weight(v.index))
- except:
- b.append(0)
-
- a = array(a)
- b = array(b)
- f = self.f
- k = self.k
- diff_a = self.diff_a
- diff_b = self.diff_b
- dt = self.dt
- n_verts = len(bm.verts)
-
- for i in range(self.steps):
-
- lap_a = zeros((n_verts))#[0]*n_verts
- lap_b = zeros((n_verts))#[0]*n_verts
- for e in bm.edges:
- id0 = e.verts[0].index
- id1 = e.verts[1].index
- lap_a[id0] += a[id1] - a[id0]
- lap_a[id1] += a[id0] - a[id1]
- lap_b[id0] += b[id1] - b[id0]
- lap_b[id1] += b[id0] - b[id1]
- ab2 = a*b**2
- a += (diff_a*lap_a - ab2 + f*(1-a))*dt
- b += (diff_b*lap_b + ab2 - (k+f)*b)*dt
-
- for i in range(n_verts):
- ob.vertex_groups['A'].add([i], a[i], 'REPLACE')
- ob.vertex_groups['B'].add([i], b[i], 'REPLACE')
- ob.vertex_groups.update()
- ob.data.update()
-
- bpy.ops.wm.redraw_timer(type='DRAW_WIN_SWAP', iterations=1)
-
- bpy.ops.object.mode_set(mode='WEIGHT_PAINT')
- return {'FINISHED'}
-
-
-class edges_deformation(bpy.types.Operator):
- bl_idname = "object.edges_deformation"
- bl_label = "Edges Deformation"
- bl_description = ("Compute Weight based on the deformation of edges "
- "according to visible modifiers")
- bl_options = {'REGISTER', 'UNDO'}
-
- bounds : bpy.props.EnumProperty(
- items=(('MANUAL', "Manual Bounds", ""),
- ('COMPRESSION', "Compressed Only", ""),
- ('TENSION', "Extended Only", ""),
- ('AUTOMATIC', "Automatic Bounds", "")),
- default='AUTOMATIC', name="Bounds")
-
- mode : bpy.props.EnumProperty(
- items=(('MAX', "Max Deformation", ""),
- ('MEAN', "Average Deformation", "")),
- default='MEAN', name="Evaluation Mode")
-
- min_def : bpy.props.FloatProperty(
- name="Min", default=0, soft_min=-1, soft_max=0,
- description="Deformations with 0 weight")
-
- max_def : bpy.props.FloatProperty(
- name="Max", default=0.5, soft_min=0, soft_max=5,
- description="Deformations with 1 weight")
-
- bounds_string = ""
-
- frame = None
-
- @classmethod
- def poll(cls, context):
- return len(context.object.modifiers) > 0
-
- def draw(self, context):
- layout = self.layout
- col = layout.column(align=True)
- col.label(text="Evaluation Mode")
- col.prop(self, "mode", text="")
- col.label(text="Bounds")
- col.prop(self, "bounds", text="")
- if self.bounds == 'MANUAL':
- col.label(text="Strain Rate \u03B5:")
- col.prop(self, "min_def")
- col.prop(self, "max_def")
- col.label(text="\u03B5" + ": from " + self.bounds_string)
-
- def execute(self, context):
- try: ob = context.object
- except:
- self.report({'ERROR'}, "Please select an Object")
- return {'CANCELLED'}
-
- # check if the object is Cloth or Softbody
- physics = False
- for m in ob.modifiers:
- if m.type == 'CLOTH' or m.type == 'SOFT_BODY':
- physics = True
- if context.scene.frame_current == 1 and self.frame != None:
- context.scene.frame_current = self.frame
- break
- if not physics: self.frame = None
-
- if self.mode == 'MEAN': group_name = "Average Deformation"
- elif self.mode == 'MAX': group_name = "Max Deformation"
- ob.vertex_groups.new(name=group_name)
- me0 = ob.data
-
- me = simple_to_mesh(ob) #ob.to_mesh(preserve_all_data_layers=True, depsgraph=bpy.context.evaluated_depsgraph_get()).copy()
- if len(me.vertices) != len(me0.vertices) or len(me.edges) != len(me0.edges):
- self.report({'ERROR'}, "The topology of the object should be" +
- "unaltered")
- return {'CANCELLED'}
-
- bm0 = bmesh.new()
- bm0.from_mesh(me0)
- bm = bmesh.new()
- bm.from_mesh(me)
- deformations = []
- for e0, e in zip(bm0.edges, bm.edges):
- try:
- l0 = e0.calc_length()
- l1 = e.calc_length()
- epsilon = (l1 - l0)/l0
- deformations.append(epsilon)
- except: deformations.append(1)
- v_deformations = []
- for v in bm.verts:
- vdef = []
- for e in v.link_edges:
- vdef.append(deformations[e.index])
- if self.mode == 'MEAN': v_deformations.append(mean(vdef))
- elif self.mode == 'MAX': v_deformations.append(max(vdef, key=abs))
- #elif self.mode == 'MIN': v_deformations.append(min(vdef, key=abs))
-
- if self.bounds == 'MANUAL':
- min_def = self.min_def
- max_def = self.max_def
- elif self.bounds == 'AUTOMATIC':
- min_def = min(v_deformations)
- max_def = max(v_deformations)
- self.min_def = min_def
- self.max_def = max_def
- elif self.bounds == 'COMPRESSION':
- min_def = 0
- max_def = min(v_deformations)
- self.min_def = min_def
- self.max_def = max_def
- elif self.bounds == 'TENSION':
- min_def = 0
- max_def = max(v_deformations)
- self.min_def = min_def
- self.max_def = max_def
- delta_def = max_def - min_def
-
- # check undeformed errors
- if delta_def == 0:
- if self.bounds == 'MANUAL':
- delta_def = 0.0001
- else:
- message = "The object doesn't have deformations."
- if physics:
- message = message + ("\nIf you are using Physics try to " +
- "save it in the cache before.")
- self.report({'ERROR'}, message)
- return {'CANCELLED'}
- else:
- if physics:
- self.frame = context.scene.frame_current
-
- for i in range(len(v_deformations)):
- weight = (v_deformations[i] - min_def)/delta_def
- ob.vertex_groups[-1].add([i], weight, 'REPLACE')
- self.bounds_string = str(round(min_def,2)) + " to " + str(round(max_def,2))
- ob.vertex_groups[-1].name = group_name + " " + self.bounds_string
- ob.vertex_groups.update()
- ob.data.update()
- bpy.ops.object.mode_set(mode='WEIGHT_PAINT')
- bpy.data.meshes.remove(me)
- return {'FINISHED'}
-
-class edges_bending(bpy.types.Operator):
- bl_idname = "object.edges_bending"
- bl_label = "Edges Bending"
- bl_description = ("Compute Weight based on the bending of edges "
- "according to visible modifiers")
- bl_options = {'REGISTER', 'UNDO'}
-
- bounds : bpy.props.EnumProperty(
- items=(('MANUAL', "Manual Bounds", ""),
- ('POSITIVE', "Positive Only", ""),
- ('NEGATIVE', "Negative Only", ""),
- ('UNSIGNED', "Absolute Bending", ""),
- ('AUTOMATIC', "Signed Bending", "")),
- default='AUTOMATIC', name="Bounds")
-
- min_def : bpy.props.FloatProperty(
- name="Min", default=-10, soft_min=-45, soft_max=45,
- description="Deformations with 0 weight")
-
- max_def : bpy.props.FloatProperty(
- name="Max", default=10, soft_min=-45, soft_max=45,
- description="Deformations with 1 weight")
-
- bounds_string = ""
- frame = None
-
- @classmethod
- def poll(cls, context):
- return len(context.object.modifiers) > 0
-
- def draw(self, context):
- layout = self.layout
- layout.label(text="Bounds")
- layout.prop(self, "bounds", text="")
- if self.bounds == 'MANUAL':
- layout.prop(self, "min_def")
- layout.prop(self, "max_def")
-
- def execute(self, context):
- try: ob = context.object
- except:
- self.report({'ERROR'}, "Please select an Object")
- return {'CANCELLED'}
-
- group_name = "Edges Bending"
- ob.vertex_groups.new(name=group_name)
-
- # check if the object is Cloth or Softbody
- physics = False
- for m in ob.modifiers:
- if m.type == 'CLOTH' or m.type == 'SOFT_BODY':
- physics = True
- if context.scene.frame_current == 1 and self.frame != None:
- context.scene.frame_current = self.frame
- break
- if not physics: self.frame = None
-
- #ob.data.update()
- #context.scene.update()
- me0 = ob.data
- me = simple_to_mesh(ob) #ob.to_mesh(preserve_all_data_layers=True, depsgraph=bpy.context.evaluated_depsgraph_get()).copy()
- if len(me.vertices) != len(me0.vertices) or len(me.edges) != len(me0.edges):
- self.report({'ERROR'}, "The topology of the object should be" +
- "unaltered")
- bm0 = bmesh.new()
- bm0.from_mesh(me0)
- bm = bmesh.new()
- bm.from_mesh(me)
- deformations = []
- for e0, e in zip(bm0.edges, bm.edges):
- try:
- ang = e.calc_face_angle_signed()
- ang0 = e0.calc_face_angle_signed()
- if self.bounds == 'UNSIGNED':
- deformations.append(abs(ang-ang0))
- else:
- deformations.append(ang-ang0)
- except: deformations.append(0)
- v_deformations = []
- for v in bm.verts:
- vdef = []
- for e in v.link_edges:
- vdef.append(deformations[e.index])
- v_deformations.append(mean(vdef))
- if self.bounds == 'MANUAL':
- min_def = radians(self.min_def)
- max_def = radians(self.max_def)
- elif self.bounds == 'AUTOMATIC':
- min_def = min(v_deformations)
- max_def = max(v_deformations)
- elif self.bounds == 'POSITIVE':
- min_def = 0
- max_def = min(v_deformations)
- elif self.bounds == 'NEGATIVE':
- min_def = 0
- max_def = max(v_deformations)
- elif self.bounds == 'UNSIGNED':
- min_def = 0
- max_def = max(v_deformations)
- delta_def = max_def - min_def
-
- # check undeformed errors
- if delta_def == 0:
- if self.bounds == 'MANUAL':
- delta_def = 0.0001
- else:
- message = "The object doesn't have deformations."
- if physics:
- message = message + ("\nIf you are using Physics try to " +
- "save it in the cache before.")
- self.report({'ERROR'}, message)
- return {'CANCELLED'}
- else:
- if physics:
- self.frame = context.scene.frame_current
-
- for i in range(len(v_deformations)):
- weight = (v_deformations[i] - min_def)/delta_def
- ob.vertex_groups[-1].add([i], weight, 'REPLACE')
- self.bounds_string = str(round(min_def,2)) + " to " + str(round(max_def,2))
- ob.vertex_groups[-1].name = group_name + " " + self.bounds_string
- ob.vertex_groups.update()
- ob.data.update()
- bpy.ops.object.mode_set(mode='WEIGHT_PAINT')
- bpy.data.meshes.remove(me)
- return {'FINISHED'}
-
-class weight_contour_displace(bpy.types.Operator):
- bl_idname = "object.weight_contour_displace"
- bl_label = "Contour Displace"
- bl_description = ("")
- bl_options = {'REGISTER', 'UNDO'}
-
- use_modifiers : bpy.props.BoolProperty(
- name="Use Modifiers", default=True,
- description="Apply all the modifiers")
- min_iso : bpy.props.FloatProperty(
- name="Min Iso Value", default=0.49, min=0, max=1,
- description="Threshold value")
- max_iso : bpy.props.FloatProperty(
- name="Max Iso Value", default=0.51, min=0, max=1,
- description="Threshold value")
- n_cuts : bpy.props.IntProperty(
- name="Cuts", default=2, min=1, soft_max=10,
- description="Number of cuts in the selected range of values")
- bool_displace : bpy.props.BoolProperty(
- name="Add Displace", default=True, description="Add Displace Modifier")
- bool_flip : bpy.props.BoolProperty(
- name="Flip", default=False, description="Flip Output Weight")
-
- weight_mode : bpy.props.EnumProperty(
- items=[('Remapped', 'Remapped', 'Remap values'),
- ('Alternate', 'Alternate', 'Alternate 0 and 1'),
- ('Original', 'Original', 'Keep original Vertex Group')],
- name="Weight", description="Choose how to convert vertex group",
- default="Remapped", options={'LIBRARY_EDITABLE'})
-
- @classmethod
- def poll(cls, context):
- return len(context.object.vertex_groups) > 0
-
- def invoke(self, context, event):
- return context.window_manager.invoke_props_dialog(self, width=350)
-
- def execute(self, context):
- start_time = timeit.default_timer()
- try:
- check = bpy.context.object.vertex_groups[0]
- except:
- self.report({'ERROR'}, "The object doesn't have Vertex Groups")
- return {'CANCELLED'}
-
- ob0 = bpy.context.object
-
- group_id = ob0.vertex_groups.active_index
- vertex_group_name = ob0.vertex_groups[group_id].name
-
- bpy.ops.object.mode_set(mode='EDIT')
- bpy.ops.mesh.select_all(action='SELECT')
- bpy.ops.object.mode_set(mode='OBJECT')
- if self.use_modifiers:
- #me0 = ob0.to_mesh(preserve_all_data_layers=True, depsgraph=bpy.context.evaluated_depsgraph_get()).copy()
- me0 = simple_to_mesh(ob0)
- else:
- me0 = ob0.data.copy()
-
- # generate new bmesh
- bm = bmesh.new()
- bm.from_mesh(me0)
- bm.verts.ensure_lookup_table()
- bm.edges.ensure_lookup_table()
- bm.faces.ensure_lookup_table()
-
- # store weight values
- weight = []
- ob = bpy.data.objects.new("temp", me0)
- for g in ob0.vertex_groups:
- ob.vertex_groups.new(name=g.name)
- for v in me0.vertices:
- try:
- weight.append(ob.vertex_groups[vertex_group_name].weight(v.index))
- except:
- weight.append(0)
-
- # define iso values
- iso_values = []
- for i_cut in range(self.n_cuts):
- delta_iso = abs(self.max_iso - self.min_iso)
- min_iso = min(self.min_iso, self.max_iso)
- max_iso = max(self.min_iso, self.max_iso)
- if delta_iso == 0: iso_val = min_iso
- elif self.n_cuts > 1: iso_val = i_cut/(self.n_cuts-1)*delta_iso + min_iso
- else: iso_val = (self.max_iso + self.min_iso)/2
- iso_values.append(iso_val)
-
- # Start Cuts Iterations
- filtered_edges = bm.edges
- for iso_val in iso_values:
- delete_edges = []
-
- faces_mask = []
- for f in bm.faces:
- w_min = 2
- w_max = 2
- for v in f.verts:
- w = weight[v.index]
- if w_min == 2:
- w_max = w_min = w
- if w > w_max: w_max = w
- if w < w_min: w_min = w
- if w_min < iso_val and w_max > iso_val:
- faces_mask.append(f)
- break
-
- #link_faces = [[f for f in e.link_faces] for e in bm.edges]
-
- #faces_todo = [f.select for f in bm.faces]
- #faces_todo = [True for f in bm.faces]
- verts = []
- edges = []
- edges_id = {}
- _filtered_edges = []
- n_verts = len(bm.verts)
- count = n_verts
- for e in filtered_edges:
- #id0 = e.vertices[0]
- #id1 = e.vertices[1]
- id0 = e.verts[0].index
- id1 = e.verts[1].index
- w0 = weight[id0]
- w1 = weight[id1]
-
- if w0 == w1: continue
- elif w0 > iso_val and w1 > iso_val:
- _filtered_edges.append(e)
- continue
- elif w0 < iso_val and w1 < iso_val: continue
- elif w0 == iso_val or w1 == iso_val:
- _filtered_edges.append(e)
- continue
- else:
- v0 = bm.verts[id0].co
- v1 = bm.verts[id1].co
- v = v0.lerp(v1, (iso_val-w0)/(w1-w0))
- if e not in delete_edges:
- delete_edges.append(e)
- verts.append(v)
- edges_id[str(id0)+"_"+str(id1)] = count
- edges_id[str(id1)+"_"+str(id0)] = count
- count += 1
- _filtered_edges.append(e)
- filtered_edges = _filtered_edges
- splitted_faces = []
-
- switch = False
- # splitting faces
- for f in faces_mask:
- # create sub-faces slots. Once a new vertex is reached it will
- # change slot, storing the next vertices for a new face.
- build_faces = [[],[]]
- #switch = False
- verts0 = [v.index for v in f.verts]
- verts1 = list(verts0)
- verts1.append(verts1.pop(0)) # shift list
- for id0, id1 in zip(verts0, verts1):
-
- # add first vertex to active slot
- build_faces[switch].append(id0)
-
- # try to split edge
- try:
- # check if the edge must be splitted
- new_vert = edges_id[str(id0)+"_"+str(id1)]
- # add new vertex
- build_faces[switch].append(new_vert)
- # if there is an open face on the other slot
- if len(build_faces[not switch]) > 0:
- # store actual face
- splitted_faces.append(build_faces[switch])
- # reset actual faces and switch
- build_faces[switch] = []
- # change face slot
- switch = not switch
- # continue previous face
- build_faces[switch].append(new_vert)
- except: pass
- if len(build_faces[not switch]) == 2:
- build_faces[not switch].append(id0)
- if len(build_faces[not switch]) > 2:
- splitted_faces.append(build_faces[not switch])
- # add last face
- splitted_faces.append(build_faces[switch])
- #del_faces.append(f.index)
-
- # adding new vertices
- for v in verts: new_vert = bm.verts.new(v)
- bm.verts.index_update()
- bm.verts.ensure_lookup_table()
- # adding new faces
- missed_faces = []
- added_faces = []
- for f in splitted_faces:
- try:
- face_verts = [bm.verts[i] for i in f]
- new_face = bm.faces.new(face_verts)
- for e in new_face.edges:
- filtered_edges.append(e)
- except:
- missed_faces.append(f)
-
- bm.faces.ensure_lookup_table()
- # updating weight values
- weight = weight + [iso_val]*len(verts)
-
- # deleting old edges/faces
- bm.edges.ensure_lookup_table()
- for e in delete_edges:
- bm.edges.remove(e)
- _filtered_edges = []
- for e in filtered_edges:
- if e not in delete_edges: _filtered_edges.append(e)
- filtered_edges = _filtered_edges
-
- name = ob0.name + '_ContourDisp'
- me = bpy.data.meshes.new(name)
- bm.to_mesh(me)
- ob = bpy.data.objects.new(name, me)
-
- # Link object to scene and make active
- scn = bpy.context.scene
- bpy.context.collection.objects.link(ob)
- bpy.context.view_layer.objects.active = ob
- ob.select_set(True)
- ob0.select_set(False)
-
- # generate new vertex group
- for g in ob0.vertex_groups:
- ob.vertex_groups.new(name=g.name)
- #ob.vertex_groups.new(name=vertex_group_name)
-
- all_weight = weight + [iso_val]*len(verts)
- #mult = 1/(1-iso_val)
- for id in range(len(all_weight)):
- #if False: w = (all_weight[id]-iso_val)*mult
- w = all_weight[id]
- if self.weight_mode == 'Alternate':
- direction = self.bool_flip
- for i in range(len(iso_values)-1):
- val0, val1 = iso_values[i], iso_values[i+1]
- if val0 < w <= val1:
- if direction: w1 = (w-val0)/(val1-val0)
- else: w1 = (val1-w)/(val1-val0)
- direction = not direction
- if w < iso_values[0]: w1 = not self.bool_flip
- if w > iso_values[-1]: w1 = not direction
- elif self.weight_mode == 'Remapped':
- if w < min_iso: w1 = 0
- elif w > max_iso: w1 = 1
- else: w1 = (w - min_iso)/delta_iso
- else:
- if self.bool_flip: w1 = 1-w
- else: w1 = w
- ob.vertex_groups[vertex_group_name].add([id], w1, 'REPLACE')
-
- ob.vertex_groups.active_index = group_id
-
- # align new object
- ob.matrix_world = ob0.matrix_world
-
- # Displace Modifier
- if self.bool_displace:
- ob.modifiers.new(type='DISPLACE', name='Displace')
- ob.modifiers["Displace"].mid_level = 0
- ob.modifiers["Displace"].strength = 0.1
- ob.modifiers['Displace'].vertex_group = vertex_group_name
-
- bpy.ops.object.mode_set(mode='EDIT')
- bpy.ops.object.mode_set(mode='WEIGHT_PAINT')
- print("Contour Displace time: " + str(timeit.default_timer() - start_time) + " sec")
-
- bpy.data.meshes.remove(me0)
-
- return {'FINISHED'}
-
-class weight_contour_mask(bpy.types.Operator):
- bl_idname = "object.weight_contour_mask"
- bl_label = "Contour Mask"
- bl_description = ("")
- bl_options = {'REGISTER', 'UNDO'}
-
- use_modifiers : bpy.props.BoolProperty(
- name="Use Modifiers", default=True,
- description="Apply all the modifiers")
- iso : bpy.props.FloatProperty(
- name="Iso Value", default=0.5, soft_min=0, soft_max=1,
- description="Threshold value")
- bool_solidify : bpy.props.BoolProperty(
- name="Solidify", default=True, description="Add Solidify Modifier")
- normalize_weight : bpy.props.BoolProperty(
- name="Normalize Weight", default=True,
- description="Normalize weight of remaining vertices")
-
- @classmethod
- def poll(cls, context):
- return len(context.object.vertex_groups) > 0
-
- def execute(self, context):
- start_time = timeit.default_timer()
- try:
- check = bpy.context.object.vertex_groups[0]
- except:
- self.report({'ERROR'}, "The object doesn't have Vertex Groups")
- return {'CANCELLED'}
-
- ob0 = bpy.context.object
-
- iso_val = self.iso
- group_id = ob0.vertex_groups.active_index
- vertex_group_name = ob0.vertex_groups[group_id].name
-
- bpy.ops.object.mode_set(mode='EDIT')
- bpy.ops.mesh.select_all(action='SELECT')
- bpy.ops.object.mode_set(mode='OBJECT')
- if self.use_modifiers:
- me0 = simple_to_mesh(ob0)#ob0.to_mesh(preserve_all_data_layers=True, depsgraph=bpy.context.evaluated_depsgraph_get()).copy()
- else:
- me0 = ob0.data.copy()
-
- # generate new bmesh
- bm = bmesh.new()
- bm.from_mesh(me0)
- bm.verts.ensure_lookup_table()
- bm.edges.ensure_lookup_table()
- bm.faces.ensure_lookup_table()
-
- # store weight values
- weight = []
- ob = bpy.data.objects.new("temp", me0)
- for g in ob0.vertex_groups:
- ob.vertex_groups.new(name=g.name)
- for v in me0.vertices:
- try:
- #weight.append(v.groups[vertex_group_name].weight)
- weight.append(ob.vertex_groups[vertex_group_name].weight(v.index))
- except:
- weight.append(0)
-
- faces_mask = []
- for f in bm.faces:
- w_min = 2
- w_max = 2
- for v in f.verts:
- w = weight[v.index]
- if w_min == 2:
- w_max = w_min = w
- if w > w_max: w_max = w
- if w < w_min: w_min = w
- if w_min < iso_val and w_max > iso_val:
- faces_mask.append(f)
- break
-
- filtered_edges = bm.edges# me0.edges
- faces_todo = [f.select for f in bm.faces]
- verts = []
- edges = []
- delete_edges = []
- edges_id = {}
- _filtered_edges = []
- n_verts = len(bm.verts)
- count = n_verts
- for e in filtered_edges:
- id0 = e.verts[0].index
- id1 = e.verts[1].index
- w0 = weight[id0]
- w1 = weight[id1]
-
- if w0 == w1: continue
- elif w0 > iso_val and w1 > iso_val:
- continue
- elif w0 < iso_val and w1 < iso_val: continue
- elif w0 == iso_val or w1 == iso_val: continue
- else:
- v0 = me0.vertices[id0].co
- v1 = me0.vertices[id1].co
- v = v0.lerp(v1, (iso_val-w0)/(w1-w0))
- delete_edges.append(e)
- verts.append(v)
- edges_id[str(id0)+"_"+str(id1)] = count
- edges_id[str(id1)+"_"+str(id0)] = count
- count += 1
-
- splitted_faces = []
-
- switch = False
- # splitting faces
- for f in faces_mask:
- # create sub-faces slots. Once a new vertex is reached it will
- # change slot, storing the next vertices for a new face.
- build_faces = [[],[]]
- #switch = False
- verts0 = list(me0.polygons[f.index].vertices)
- verts1 = list(verts0)
- verts1.append(verts1.pop(0)) # shift list
- for id0, id1 in zip(verts0, verts1):
-
- # add first vertex to active slot
- build_faces[switch].append(id0)
-
- # try to split edge
- try:
- # check if the edge must be splitted
- new_vert = edges_id[str(id0)+"_"+str(id1)]
- # add new vertex
- build_faces[switch].append(new_vert)
- # if there is an open face on the other slot
- if len(build_faces[not switch]) > 0:
- # store actual face
- splitted_faces.append(build_faces[switch])
- # reset actual faces and switch
- build_faces[switch] = []
- # change face slot
- switch = not switch
- # continue previous face
- build_faces[switch].append(new_vert)
- except: pass
- if len(build_faces[not switch]) == 2:
- build_faces[not switch].append(id0)
- if len(build_faces[not switch]) > 2:
- splitted_faces.append(build_faces[not switch])
- # add last face
- splitted_faces.append(build_faces[switch])
-
- # adding new vertices
- for v in verts: bm.verts.new(v)
- bm.verts.ensure_lookup_table()
-
- # deleting old edges/faces
- bm.edges.ensure_lookup_table()
- remove_edges = []
- for e in delete_edges: bm.edges.remove(e)
-
- bm.verts.ensure_lookup_table()
- # adding new faces
- missed_faces = []
- for f in splitted_faces:
- try:
- face_verts = [bm.verts[i] for i in f]
- bm.faces.new(face_verts)
- except:
- missed_faces.append(f)
-
- # Mask geometry
- if(True):
- all_weight = weight + [iso_val+0.0001]*len(verts)
- weight = []
- for w, v in zip(all_weight, bm.verts):
- if w < iso_val: bm.verts.remove(v)
- else: weight.append(w)
-
- # Create mesh and object
- name = ob0.name + '_ContourMask_{:.3f}'.format(iso_val)
- me = bpy.data.meshes.new(name)
- bm.to_mesh(me)
- ob = bpy.data.objects.new(name, me)
-
- # Link object to scene and make active
- scn = bpy.context.scene
- bpy.context.collection.objects.link(ob)
- bpy.context.view_layer.objects.active = ob
- ob.select_set(True)
- ob0.select_set(False)
-
- # generate new vertex group
- for g in ob0.vertex_groups:
- ob.vertex_groups.new(name=g.name)
-
- if iso_val != 1: mult = 1/(1-iso_val)
- else: mult = 1
- for id in range(len(weight)):
- if self.normalize_weight: w = (weight[id]-iso_val)*mult
- else: w = weight[id]
- ob.vertex_groups[vertex_group_name].add([id], w, 'REPLACE')
- ob.vertex_groups.active_index = group_id
-
- # align new object
- ob.matrix_world = ob0.matrix_world
-
- # Add Solidify
- if self.bool_solidify and True:
- ob.modifiers.new(type='SOLIDIFY', name='Solidify')
- ob.modifiers['Solidify'].thickness = 0.05
- ob.modifiers['Solidify'].offset = 0
- ob.modifiers['Solidify'].vertex_group = vertex_group_name
-
- bpy.ops.object.mode_set(mode='EDIT')
- bpy.ops.object.mode_set(mode='WEIGHT_PAINT')
- print("Contour Mask time: " + str(timeit.default_timer() - start_time) + " sec")
-
- bpy.data.meshes.remove(me0)
-
- return {'FINISHED'}
-
-class weight_contour_curves(bpy.types.Operator):
- bl_idname = "object.weight_contour_curves"
- bl_label = "Contour Curves"
- bl_description = ("")
- bl_options = {'REGISTER', 'UNDO'}
-
- use_modifiers : bpy.props.BoolProperty(
- name="Use Modifiers", default=True,
- description="Apply all the modifiers")
-
- min_iso : bpy.props.FloatProperty(
- name="Min Value", default=0., soft_min=0, soft_max=1,
- description="Minimum weight value")
- max_iso : bpy.props.FloatProperty(
- name="Max Value", default=1, soft_min=0, soft_max=1,
- description="Maximum weight value")
- n_curves : bpy.props.IntProperty(
- name="Curves", default=3, soft_min=1, soft_max=10,
- description="Number of Contour Curves")
-
- min_rad : bpy.props.FloatProperty(
- name="Min Radius", default=0.25, soft_min=0, soft_max=1,
- description="Minimum Curve Radius")
- max_rad : bpy.props.FloatProperty(
- name="Max Radius", default=0.75, soft_min=0, soft_max=1,
- description="Maximum Curve Radius")
-
- @classmethod
- def poll(cls, context):
- ob = context.object
- return len(ob.vertex_groups) > 0 or ob.type == 'CURVE'
-
- def invoke(self, context, event):
- return context.window_manager.invoke_props_dialog(self, width=350)
-
- def execute(self, context):
- start_time = timeit.default_timer()
- try:
- check = bpy.context.object.vertex_groups[0]
- except:
- self.report({'ERROR'}, "The object doesn't have Vertex Groups")
- return {'CANCELLED'}
- ob0 = bpy.context.object
-
- group_id = ob0.vertex_groups.active_index
- vertex_group_name = ob0.vertex_groups[group_id].name
-
- bpy.ops.object.mode_set(mode='EDIT')
- bpy.ops.mesh.select_all(action='SELECT')
- bpy.ops.object.mode_set(mode='OBJECT')
- if self.use_modifiers:
- me0 = simple_to_mesh(ob0) #ob0.to_mesh(preserve_all_data_layers=True, depsgraph=bpy.context.evaluated_depsgraph_get()).copy()
- else:
- me0 = ob0.data.copy()
-
- # generate new bmesh
- bm = bmesh.new()
- bm.from_mesh(me0)
- bm.verts.ensure_lookup_table()
- bm.edges.ensure_lookup_table()
- bm.faces.ensure_lookup_table()
-
- # store weight values
- weight = []
- ob = bpy.data.objects.new("temp", me0)
- for g in ob0.vertex_groups:
- ob.vertex_groups.new(name=g.name)
- for v in me0.vertices:
- try:
- #weight.append(v.groups[vertex_group_name].weight)
- weight.append(ob.vertex_groups[vertex_group_name].weight(v.index))
- except:
- weight.append(0)
-
- filtered_edges = bm.edges
- total_verts = []
- total_segments = []
- radius = []
-
- # start iterate contours levels
- for c in range(self.n_curves):
- min_iso = min(self.min_iso, self.max_iso)
- max_iso = max(self.min_iso, self.max_iso)
- try:
- iso_val = c*(max_iso-min_iso)/(self.n_curves-1)+min_iso
- if iso_val < 0: iso_val = (min_iso + max_iso)/2
- except:
- iso_val = (min_iso + max_iso)/2
- faces_mask = []
- for f in bm.faces:
- w_min = 2
- w_max = 2
- for v in f.verts:
- w = weight[v.index]
- if w_min == 2:
- w_max = w_min = w
- if w > w_max: w_max = w
- if w < w_min: w_min = w
- if w_min < iso_val and w_max > iso_val:
- faces_mask.append(f)
- break
-
- faces_todo = [f.select for f in bm.faces]
- verts = []
-
- edges_id = {}
- _filtered_edges = []
- n_verts = len(bm.verts)
- count = len(total_verts)
- for e in filtered_edges:
- id0 = e.verts[0].index
- id1 = e.verts[1].index
- w0 = weight[id0]
- w1 = weight[id1]
-
- if w0 == w1: continue
- elif w0 > iso_val and w1 > iso_val:
- _filtered_edges.append(e)
- continue
- elif w0 < iso_val and w1 < iso_val: continue
- elif w0 == iso_val or w1 == iso_val:
- _filtered_edges.append(e)
- continue
- else:
- #v0 = me0.vertices[id0].select = True
- #v1 = me0.vertices[id1].select = True
- v0 = me0.vertices[id0].co
- v1 = me0.vertices[id1].co
- v = v0.lerp(v1, (iso_val-w0)/(w1-w0))
- verts.append(v)
- edges_id[e.index] = count
- count += 1
- _filtered_edges.append(e)
- filtered_edges = _filtered_edges
-
- if len(verts) == 0: continue
-
- # finding segments
- segments = []
- for f in faces_mask:
- seg = []
- for e in f.edges:
- try:
- seg.append(edges_id[e.index])
- if len(seg) == 2:
- segments.append(seg)
- seg = []
- except: pass
-
- total_segments = total_segments + segments
- total_verts = total_verts + verts
-
- # Radius
-
- try:
- iso_rad = c*(self.max_rad-self.min_rad)/(self.n_curves-1)+self.min_rad
- if iso_rad < 0: iso_rad = (self.min_rad + self.max_rad)/2
- except:
- iso_rad = (self.min_rad + self.max_rad)/2
- radius = radius + [iso_rad]*len(verts)
-
- bm = bmesh.new()
- # adding new vertices
- for v in total_verts: bm.verts.new(v)
- bm.verts.ensure_lookup_table()
-
- # adding new edges
- for s in total_segments:
- try:
- pts = [bm.verts[i] for i in s]
- bm.edges.new(pts)
- except: pass
-
- try:
- name = ob0.name + '_ContourCurves'
- me = bpy.data.meshes.new(name)
- bm.to_mesh(me)
- ob = bpy.data.objects.new(name, me)
-
- # Link object to scene and make active
- scn = bpy.context.scene
- bpy.context.collection.objects.link(ob)
- bpy.context.view_layer.objects.active = ob
- ob.select_set(True)
- ob0.select_set(False)
-
- bpy.ops.object.convert(target='CURVE')
- ob = context.object
- count = 0
- for s in ob.data.splines:
- for p in s.points:
- p.radius = radius[count]
- count += 1
- ob.data.bevel_depth = 0.01
- ob.data.fill_mode = 'FULL'
- ob.data.bevel_resolution = 3
- except:
- self.report({'ERROR'}, "There are no values in the chosen range")
- return {'CANCELLED'}
-
- # align new object
- ob.matrix_world = ob0.matrix_world
- print("Contour Curves time: " + str(timeit.default_timer() - start_time) + " sec")
-
- bpy.data.meshes.remove(me0)
- bpy.data.meshes.remove(me)
-
- return {'FINISHED'}
-
-class vertex_colors_to_vertex_groups(bpy.types.Operator):
- bl_idname = "object.vertex_colors_to_vertex_groups"
- bl_label = "Vertex Color"
- bl_options = {'REGISTER', 'UNDO'}
- bl_description = ("Convert the active Vertex Color into a Vertex Group")
-
- red : bpy.props.BoolProperty(
- name="red channel", default=False, description="convert red channel")
- green : bpy.props.BoolProperty(
- name="green channel", default=False,
- description="convert green channel")
- blue : bpy.props.BoolProperty(
- name="blue channel", default=False, description="convert blue channel")
- value : bpy.props.BoolProperty(
- name="value channel", default=True, description="convert value channel")
- invert : bpy.props.BoolProperty(
- name="invert", default=False, description="invert all color channels")
-
- @classmethod
- def poll(cls, context):
- return len(context.object.data.vertex_colors) > 0
-
- def execute(self, context):
- obj = bpy.context.active_object
- id = len(obj.vertex_groups)
- id_red = id
- id_green = id
- id_blue = id
- id_value = id
-
- boolCol = len(obj.data.vertex_colors)
- if(boolCol): col_name = obj.data.vertex_colors.active.name
- bpy.ops.object.mode_set(mode='EDIT')
- bpy.ops.mesh.select_all(action='SELECT')
-
- if(self.red and boolCol):
- bpy.ops.object.vertex_group_add()
- bpy.ops.object.vertex_group_assign()
- id_red = id
- obj.vertex_groups[id_red].name = col_name + '_red'
- id+=1
- if(self.green and boolCol):
- bpy.ops.object.vertex_group_add()
- bpy.ops.object.vertex_group_assign()
- id_green = id
- obj.vertex_groups[id_green].name = col_name + '_green'
- id+=1
- if(self.blue and boolCol):
- bpy.ops.object.vertex_group_add()
- bpy.ops.object.vertex_group_assign()
- id_blue = id
- obj.vertex_groups[id_blue].name = col_name + '_blue'
- id+=1
- if(self.value and boolCol):
- bpy.ops.object.vertex_group_add()
- bpy.ops.object.vertex_group_assign()
- id_value = id
- obj.vertex_groups[id_value].name = col_name + '_value'
- id+=1
-
- mult = 1
- if(self.invert): mult = -1
- bpy.ops.object.mode_set(mode='OBJECT')
- sub_red = 1 + self.value + self.blue + self.green
- sub_green = 1 + self.value + self.blue
- sub_blue = 1 + self.value
- sub_value = 1
-
- id = len(obj.vertex_groups)
- if(id_red <= id and id_green <= id and id_blue <= id and id_value <= \
- id and boolCol):
- v_colors = obj.data.vertex_colors.active.data
- i = 0
- for f in obj.data.polygons:
- for v in f.vertices:
- gr = obj.data.vertices[v].groups
- if(self.red): gr[min(len(gr)-sub_red, id_red)].weight = \
- self.invert + mult * v_colors[i].color[0]
- if(self.green): gr[min(len(gr)-sub_green, id_green)].weight\
- = self.invert + mult * v_colors[i].color[1]
- if(self.blue): gr[min(len(gr)-sub_blue, id_blue)].weight = \
- self.invert + mult * v_colors[i].color[2]
- if(self.value):
- r = v_colors[i].color[0]
- g = v_colors[i].color[1]
- b = v_colors[i].color[2]
- gr[min(len(gr)-sub_value, id_value)].weight\
- = self.invert + mult * (0.2126*r + 0.7152*g + 0.0722*b)
- i+=1
- bpy.ops.paint.weight_paint_toggle()
- return {'FINISHED'}
-
-class vertex_group_to_vertex_colors(bpy.types.Operator):
- bl_idname = "object.vertex_group_to_vertex_colors"
- bl_label = "Vertex Group"
- bl_options = {'REGISTER', 'UNDO'}
- bl_description = ("Convert the active Vertex Group into a Vertex Color")
-
- channel : bpy.props.EnumProperty(
- items=[('Blue', 'Blue Channel', 'Convert to Blue Channel'),
- ('Green', 'Green Channel', 'Convert to Green Channel'),
- ('Red', 'Red Channel', 'Convert to Red Channel'),
- ('Value', 'Value Channel', 'Convert to Grayscale'),
- ('False Colors', 'False Colors', 'Convert to False Colors')],
- name="Convert to", description="Choose how to convert vertex group",
- default="Value", options={'LIBRARY_EDITABLE'})
-
- invert : bpy.props.BoolProperty(
- name="invert", default=False, description="invert color channel")
-
- @classmethod
- def poll(cls, context):
- return len(context.object.vertex_groups) > 0
-
- def execute(self, context):
- obj = bpy.context.active_object
- group_id = obj.vertex_groups.active_index
- if (group_id == -1):
- return {'FINISHED'}
-
- bpy.ops.object.mode_set(mode='OBJECT')
- group_name = obj.vertex_groups[group_id].name
- bpy.ops.mesh.vertex_color_add()
- colors_id = obj.data.vertex_colors.active_index
-
- colors_name = group_name
- if(self.channel == 'False Colors'): colors_name += "_false_colors"
- elif(self.channel == 'Value'): colors_name += "_value"
- elif(self.channel == 'Red'): colors_name += "_red"
- elif(self.channel == 'Green'): colors_name += "_green"
- elif(self.channel == 'Blue'): colors_name += "_blue"
- bpy.context.object.data.vertex_colors[colors_id].name = colors_name
-
- v_colors = obj.data.vertex_colors.active.data
-
- mult = 1
- if(self.invert): mult = -1
-
- i = 0
- for f in obj.data.polygons:
- for v in f.vertices:
- gr = obj.data.vertices[v].groups
-
- if(self.channel == 'False Colors'): v_colors[i].color = (0,0,0.5,1)
- else: v_colors[i].color = (0,0,0,1)
-
- for g in gr:
- if g.group == group_id:
- w = g.weight
- if(self.channel == 'False Colors'):
- mult = 0.6+0.4*w
- if w < 0.25:
- v_colors[i].color = (0, w*4*mult, 1*mult,1)
- elif w < 0.5:
- v_colors[i].color = (0, 1*mult, (1-(w-0.25)*4)*mult,1)
- elif w < 0.75:
- v_colors[i].color = ((w-0.5)*4*mult,1*mult,0,1)
- else:
- v_colors[i].color = (1*mult,(1-(w-0.75)*4)*mult,0,1)
- elif(self.channel == 'Value'):
- v_colors[i].color = (
- self.invert + mult * w,
- self.invert + mult * w,
- self.invert + mult * w,
- 1)
- elif(self.channel == 'Red'):
- v_colors[i].color = (
- self.invert + mult * w,0,0,1)
- elif(self.channel == 'Green'):
- v_colors[i].color = (
- 0, self.invert + mult * w,0,1)
- elif(self.channel == 'Blue'):
- v_colors[i].color = (
- 0,0, self.invert + mult * w,1)
- i+=1
- bpy.ops.paint.vertex_paint_toggle()
- bpy.context.object.data.vertex_colors[colors_id].active_render = True
- return {'FINISHED'}
-
-class curvature_to_vertex_groups(bpy.types.Operator):
- bl_idname = "object.curvature_to_vertex_groups"
- bl_label = "Curvature"
- bl_options = {'REGISTER', 'UNDO'}
- bl_description = ("Generate a Vertex Group based on the curvature of the "
- "mesh. Is based on Dirty Vertex Color")
-
- invert : bpy.props.BoolProperty(
- name="invert", default=False, description="invert values")
-
- blur_strength : bpy.props.FloatProperty(
- name="Blur Strength", default=1, min=0.001,
- max=1, description="Blur strength per iteration")
-
- blur_iterations : bpy.props.IntProperty(
- name="Blur Iterations", default=1, min=0,
- max=40, description="Number of times to blur the values")
-
- min_angle : bpy.props.FloatProperty(
- name="Min Angle", default=0, min=0,
- max=pi/2, subtype='ANGLE', description="Minimum angle")
-
- max_angle : bpy.props.FloatProperty(
- name="Max Angle", default=pi, min=pi/2,
- max=pi, subtype='ANGLE', description="Maximum angle")
-
- invert : bpy.props.BoolProperty(
- name="Invert", default=False,
- description="Invert the curvature map")
-
- def execute(self, context):
- bpy.ops.object.mode_set(mode='OBJECT')
- bpy.ops.mesh.vertex_color_add()
- vertex_colors = bpy.context.active_object.data.vertex_colors
- vertex_colors[-1].active = True
- vertex_colors[-1].active_render = True
- vertex_colors[-1].name = "Curvature"
- for c in vertex_colors[-1].data: c.color = (1,1,1,1)
- bpy.ops.object.mode_set(mode='VERTEX_PAINT')
- bpy.ops.paint.vertex_color_dirt(
- blur_strength=self.blur_strength,
- blur_iterations=self.blur_iterations, clean_angle=self.max_angle,
- dirt_angle=self.min_angle)
- bpy.ops.object.vertex_colors_to_vertex_groups(invert=self.invert)
- bpy.ops.mesh.vertex_color_remove()
- return {'FINISHED'}
-
-
-class face_area_to_vertex_groups(bpy.types.Operator):
- bl_idname = "object.face_area_to_vertex_groups"
- bl_label = "Area"
- bl_options = {'REGISTER', 'UNDO'}
- bl_description = ("Generate a Vertex Group based on the area of individual "
- "faces")
-
- invert : bpy.props.BoolProperty(
- name="invert", default=False, description="invert values")
- bounds : bpy.props.EnumProperty(
- items=(('MANUAL', "Manual Bounds", ""),
- ('AUTOMATIC', "Automatic Bounds", "")),
- default='AUTOMATIC', name="Bounds")
-
- min_area : bpy.props.FloatProperty(
- name="Min", default=0.01, soft_min=0, soft_max=1,
- description="Faces with 0 weight")
-
- max_area : bpy.props.FloatProperty(
- name="Max", default=0.1, soft_min=0, soft_max=1,
- description="Faces with 1 weight")
-
- def draw(self, context):
- layout = self.layout
- layout.label(text="Bounds")
- layout.prop(self, "bounds", text="")
- if self.bounds == 'MANUAL':
- layout.prop(self, "min_area")
- layout.prop(self, "max_area")
-
- def execute(self, context):
- try: ob = context.object
- except:
- self.report({'ERROR'}, "Please select an Object")
- return {'CANCELLED'}
- ob.vertex_groups.new(name="Faces Area")
-
- areas = [[] for v in ob.data.vertices]
-
- for p in ob.data.polygons:
- for v in p.vertices:
- areas[v].append(p.area)
-
- for i in range(len(areas)):
- areas[i] = mean(areas[i])
- if self.bounds == 'MANUAL':
- min_area = self.min_area
- max_area = self.max_area
- elif self.bounds == 'AUTOMATIC':
- min_area = min(areas)
- max_area = max(areas)
- elif self.bounds == 'COMPRESSION':
- min_area = 1
- max_area = min(areas)
- elif self.bounds == 'TENSION':
- min_area = 1
- max_area = max(areas)
- delta_area = max_area - min_area
- if delta_area == 0:
- delta_area = 0.0001
- if self.bounds == 'MANUAL':
- delta_area = 0.0001
- else:
- self.report({'ERROR'}, "The faces have the same areas")
- #return {'CANCELLED'}
- for i in range(len(areas)):
- weight = (areas[i] - min_area)/delta_area
- ob.vertex_groups[-1].add([i], weight, 'REPLACE')
- ob.vertex_groups.update()
- ob.data.update()
- bpy.ops.object.mode_set(mode='WEIGHT_PAINT')
- return {'FINISHED'}
-
-
-class harmonic_weight(bpy.types.Operator):
- bl_idname = "object.harmonic_weight"
- bl_label = "Harmonic"
- bl_options = {'REGISTER', 'UNDO'}
- bl_description = ("Create an harmonic variation of the active Vertex Group")
-
- freq : bpy.props.FloatProperty(
- name="Frequency", default=20, soft_min=0,
- soft_max=100, description="Wave frequency")
-
- amp : bpy.props.FloatProperty(
- name="Amplitude", default=1, soft_min=0,
- soft_max=10, description="Wave amplitude")
-
- midlevel : bpy.props.FloatProperty(
- name="Midlevel", default=0, min=-1,
- max=1, description="Midlevel")
-
- add : bpy.props.FloatProperty(
- name="Add", default=0, min=-1,
- max=1, description="Add to the Weight")
-
- mult : bpy.props.FloatProperty(
- name="Multiply", default=0, min=0,
- max=1, description="Multiply for he Weight")
-
- @classmethod
- def poll(cls, context):
- return len(context.object.vertex_groups) > 0
-
- def execute(self, context):
- ob = bpy.context.active_object
- if len(ob.vertex_groups) > 0:
- group_id = ob.vertex_groups.active_index
- ob.vertex_groups.new(name="Harmonic")
- for i in range(len(ob.data.vertices)):
- try: val = ob.vertex_groups[group_id].weight(i)
- except: val = 0
- weight = self.amp*(sin(val*self.freq) - self.midlevel)/2 + 0.5 + self.add*val*(1-(1-val)*self.mult)
- ob.vertex_groups[-1].add([i], weight, 'REPLACE')
- ob.data.update()
- else:
- self.report({'ERROR'}, "Active object doesn't have vertex groups")
- return {'CANCELLED'}
- bpy.ops.object.mode_set(mode='WEIGHT_PAINT')
- return {'FINISHED'}
-
-
-
-class TISSUE_PT_color(bpy.types.Panel):
- bl_label = "Tissue Tools"
- bl_category = "Tissue"
- bl_space_type = "VIEW_3D"
- bl_region_type = "UI"
- #bl_options = {'DEFAULT_CLOSED'}
- bl_context = "vertexpaint"
-
- def draw(self, context):
- layout = self.layout
- col = layout.column(align=True)
- col.operator("object.vertex_colors_to_vertex_groups",
- icon="GROUP_VERTEX", text="Convert to Weight")
-
-class TISSUE_PT_weight(bpy.types.Panel):
- bl_label = "Tissue Tools"
- bl_category = "Tissue"
- bl_space_type = "VIEW_3D"
- bl_region_type = "UI"
- #bl_options = {'DEFAULT_CLOSED'}
- bl_context = "weightpaint"
-
- def draw(self, context):
- layout = self.layout
- col = layout.column(align=True)
- #if context.object.type == 'MESH' and context.mode == 'OBJECT':
- #col.label(text="Transform:")
- #col.separator()
- #elif bpy.context.mode == 'PAINT_WEIGHT':
- col.label(text="Weight Generate:")
- #col.operator(
- # "object.vertex_colors_to_vertex_groups", icon="GROUP_VCOL")
- col.operator("object.face_area_to_vertex_groups", icon="FACESEL")
- col.operator("object.curvature_to_vertex_groups", icon="SMOOTHCURVE")
- try: col.operator("object.weight_formula", icon="CON_TRANSFORM")
- except: col.operator("object.weight_formula")#, icon="CON_TRANSFORM")
- #col.label(text="Weight Processing:")
- col.separator()
-
- # TO BE FIXED
- #col.operator("object.weight_laplacian", icon="SMOOTHCURVE")
-
- col.operator("object.harmonic_weight", icon="IPO_ELASTIC")
- col.operator("object.vertex_group_to_vertex_colors", icon="GROUP_VCOL",
- text="Convert to Colors")
- col.separator()
- col.label(text="Deformation Analysis:")
- col.operator("object.edges_deformation", icon="DRIVER_DISTANCE")#FULLSCREEN_ENTER")
- col.operator("object.edges_bending", icon="DRIVER_ROTATIONAL_DIFFERENCE")#"MOD_SIMPLEDEFORM")
- col.separator()
- col.label(text="Weight Contour:")
- col.operator("object.weight_contour_curves", icon="MOD_CURVE")
- col.operator("object.weight_contour_displace", icon="MOD_DISPLACE")
- col.operator("object.weight_contour_mask", icon="MOD_MASK")
- col.separator()
- col.label(text="Simulations:")
- #col.operator("object.reaction_diffusion", icon="MOD_OCEAN")
- col.operator("object.start_reaction_diffusion",
- icon="EXPERIMENTAL",
- text="Reaction-Diffusion")
-
- #col.prop(context.object, "reaction_diffusion_run", icon="PLAY", text="Run Simulation")
- ####col.prop(context.object, "reaction_diffusion_run")
- #col.separator()
- #col.label(text="Vertex Color from:")
- #col.operator("object.vertex_group_to_vertex_colors", icon="GROUP_VERTEX")
-
-
-
-
-class start_reaction_diffusion(bpy.types.Operator):
- bl_idname = "object.start_reaction_diffusion"
- bl_label = "Start Reaction Diffusion"
- bl_description = ("Run a Reaction-Diffusion based on existing Vertex Groups: A and B")
- bl_options = {'REGISTER', 'UNDO'}
-
- run : bpy.props.BoolProperty(
- name="Run Reaction-Diffusion", default=True, description="Compute a new iteration on frame changes")
-
- time_steps : bpy.props.IntProperty(
- name="Steps", default=10, min=0, soft_max=50,
- description="Number of Steps")
-
- dt : bpy.props.FloatProperty(
- name="dt", default=1, min=0, soft_max=0.2,
- description="Time Step")
-
- diff_a : bpy.props.FloatProperty(
- name="Diff A", default=0.18, min=0, soft_max=2,
- description="Diffusion A")
-
- diff_b : bpy.props.FloatProperty(
- name="Diff B", default=0.09, min=0, soft_max=2,
- description="Diffusion B")
-
- f : bpy.props.FloatProperty(
- name="f", default=0.055, min=0, soft_max=0.5, precision=4,
- description="Feed Rate")
-
- k : bpy.props.FloatProperty(
- name="k", default=0.062, min=0, soft_max=0.5, precision=4,
- description="Kill Rate")
-
- @classmethod
- def poll(cls, context):
- return context.object.type == 'MESH'
-
- def execute(self, context):
- reaction_diffusion_add_handler(self, context)
- set_animatable_fix_handler(self, context)
-
- ob = context.object
-
- ob.reaction_diffusion_settings.run = self.run
- ob.reaction_diffusion_settings.dt = self.dt
- ob.reaction_diffusion_settings.time_steps = self.time_steps
- ob.reaction_diffusion_settings.f = self.f
- ob.reaction_diffusion_settings.k = self.k
- ob.reaction_diffusion_settings.diff_a = self.diff_a
- ob.reaction_diffusion_settings.diff_b = self.diff_b
-
-
- # check vertex group A
- try:
- vg = ob.vertex_groups['A']
- except:
- ob.vertex_groups.new(name='A')
- # check vertex group B
- try:
- vg = ob.vertex_groups['B']
- except:
- ob.vertex_groups.new(name='B')
-
- for v in ob.data.vertices:
- ob.vertex_groups['A'].add([v.index], 1, 'REPLACE')
- ob.vertex_groups['B'].add([v.index], 0, 'REPLACE')
-
- ob.vertex_groups.update()
- ob.data.update()
- bpy.ops.object.mode_set(mode='WEIGHT_PAINT')
-
- return {'FINISHED'}
-
-class reset_reaction_diffusion_weight(bpy.types.Operator):
- bl_idname = "object.reset_reaction_diffusion_weight"
- bl_label = "Reset Reaction Diffusion Weight"
- bl_description = ("Set A and B weight to default values")
- bl_options = {'REGISTER', 'UNDO'}
-
- @classmethod
- def poll(cls, context):
- return context.object.type == 'MESH'
-
- def execute(self, context):
- reaction_diffusion_add_handler(self, context)
- set_animatable_fix_handler(self, context)
-
- ob = context.object
-
- # check vertex group A
- try:
- vg = ob.vertex_groups['A']
- except:
- ob.vertex_groups.new(name='A')
- # check vertex group B
- try:
- vg = ob.vertex_groups['B']
- except:
- ob.vertex_groups.new(name='B')
-
- for v in ob.data.vertices:
- ob.vertex_groups['A'].add([v.index], 1, 'REPLACE')
- ob.vertex_groups['B'].add([v.index], 0, 'REPLACE')
-
- ob.vertex_groups.update()
- ob.data.update()
- bpy.ops.object.mode_set(mode='WEIGHT_PAINT')
-
- return {'FINISHED'}
-
-from bpy.app.handlers import persistent
-
-@persistent
-def reaction_diffusion_def_blur(scene):
- for ob in scene.objects:
- if ob.reaction_diffusion_settings.run:
- #try:
- me = ob.data
- bm = bmesh.new()
- bm.from_mesh(me)
- bm.edges.ensure_lookup_table()
-
- # store weight values
- a = []
- b = []
- for v in me.vertices:
- try:
- a.append(ob.vertex_groups["A"].weight(v.index))
- except:
- a.append(0)
- try:
- b.append(ob.vertex_groups["B"].weight(v.index))
- except:
- b.append(0)
-
- a = array(a)
- b = array(b)
- props = ob.reaction_diffusion_settings
- dt = props.dt
- time_steps = props.time_steps
- f = props.f
- k = props.k
- diff_a = props.diff_a * props.diff_mult
- diff_b = props.diff_b * props.diff_mult
-
- n_verts = len(bm.verts)
- #bpy.ops.object.mode_set(mode='WEIGHT_PAINT')
- #ob.data.use_paint_mask_vertex = True
-
- for i in range(time_steps):
- ab2 = a*b**2
- ob.vertex_groups.active = ob.vertex_groups['A']
- bpy.ops.object.vertex_group_smooth(group_select_mode='ACTIVE', factor=diff_a)
- ob.vertex_groups.active = ob.vertex_groups['B']
- bpy.ops.object.vertex_group_smooth(group_select_mode='ACTIVE', factor=diff_b)
-
- a = []
- b = []
- for v in me.vertices:
- a.append(ob.vertex_groups["A"].weight(v.index))
- b.append(ob.vertex_groups["B"].weight(v.index))
- a = array(a)
- b = array(b)
-
- a += - (ab2 + f*(1-a))*dt
- b += (ab2 - (k+f)*b)*dt
-
- a = nan_to_num(a)
- b = nan_to_num(b)
-
- for i in range(n_verts):
- ob.vertex_groups['A'].add([i], a[i], 'REPLACE')
- ob.vertex_groups['B'].add([i], b[i], 'REPLACE')
- ob.vertex_groups.update()
- ob.data.update()
- #bpy.ops.object.mode_set(mode='EDIT')
- #bpy.ops.object.mode_set(mode='WEIGHT_PAINT
- #bpy.ops.paint.weight_paint_toggle()
- #bpy.ops.paint.weight_paint_toggle()
-
- #bpy.ops.object.mode_set(mode='WEIGHT_PAINT')
- #except:
- # pass
-
-def reaction_diffusion_def_(scene):
- for ob in scene.objects:
- if ob.reaction_diffusion_settings.run:
- #try:
- me = ob.data
- bm = bmesh.new()
- bm.from_mesh(me)
- bm.edges.ensure_lookup_table()
-
- # store weight values
- a = []
- b = []
- for v in me.vertices:
- try:
- a.append(ob.vertex_groups["A"].weight(v.index))
- except:
- a.append(0)
- try:
- b.append(ob.vertex_groups["B"].weight(v.index))
- except:
- b.append(0)
-
- a = array(a)
- b = array(b)
- props = ob.reaction_diffusion_settings
- dt = props.dt
- time_steps = props.time_steps
- f = props.f
- k = props.k
- diff_a = props.diff_a * props.diff_mult
- diff_b = props.diff_b * props.diff_mult
-
- n_verts = len(bm.verts)
- for i in range(time_steps):
- lap_a = zeros((n_verts))#[0]*n_verts
- lap_b = zeros((n_verts))#[0]*n_verts
- if i == 0:
- lap_map = [[] for i in range(n_verts)]
- lap_mult = []
- for e in bm.edges:
- id0 = e.verts[0].index
- id1 = e.verts[1].index
- lap_map[id0].append(id1)
- lap_map[id1].append(id0)
- for id in range(n_verts):
- lap_mult.append(len(lap_map[id]))
- lap_mult = array(lap_mult)
- lap_map = array(lap_map)
- for id in range(n_verts):
- map = lap_map[id]
- lap_a[id] = a[lap_map[id]].sum()
- lap_b[id] = b[lap_map[id]].sum()
- lap_a -= a*lap_mult
- lap_b -= b*lap_mult
- ab2 = a*b**2
-
- a += (diff_a*lap_a - ab2 + f*(1-a))*dt
- b += (diff_b*lap_b + ab2 - (k+f)*b)*dt
-
- a = nan_to_num(a)
- b = nan_to_num(b)
-
- for i in range(n_verts):
- ob.vertex_groups['A'].add([i], a[i], 'REPLACE')
- ob.vertex_groups['B'].add([i], b[i], 'REPLACE')
- ob.vertex_groups.update()
- ob.data.update()
- #bpy.ops.object.mode_set(mode='EDIT')
- #bpy.ops.object.mode_set(mode='WEIGHT_PAINT')
- bpy.ops.paint.weight_paint_toggle()
- bpy.ops.paint.weight_paint_toggle()
-
- #bpy.ops.object.mode_set(mode='WEIGHT_PAINT')
- #except:
- # pass
-
-def reaction_diffusion_def(scene):
- for ob in scene.objects:
- if ob.reaction_diffusion_settings.run:
-
- start = time.time()
-
- me = ob.data
- n_edges = len(me.edges)
- n_verts = len(me.vertices)
-
- # store weight values
- a = np.zeros(n_verts)
- b = np.zeros(n_verts)
- #a = thread_read_weight(a, ob.vertex_groups["A"])
- #b = thread_read_weight(b, ob.vertex_groups["B"])
- #a = read_weight(a, ob.vertex_groups["A"])
- #b = read_weight(b, ob.vertex_groups["B"])
-
- for i in range(n_verts):
- try: a[i] = ob.vertex_groups["A"].weight(i)
- except: pass
- try: b[i] = ob.vertex_groups["B"].weight(i)
- except: pass
-
- props = ob.reaction_diffusion_settings
- dt = props.dt
- time_steps = props.time_steps
- f = props.f
- k = props.k
- diff_a = props.diff_a * props.diff_mult
- diff_b = props.diff_b * props.diff_mult
-
- edge_verts = [0]*n_edges*2
- me.edges.foreach_get("vertices", edge_verts)
-
- timeElapsed = time.time() - start
- print('RD - Preparation Time:',timeElapsed)
- start = time.time()
-
- try:
- edge_verts = np.array(edge_verts)
- a, b = numba_reaction_diffusion(n_verts, n_edges, edge_verts, a, b, diff_a, diff_b, f, k, dt, time_steps)
- a = nan_to_num(a)
- b = nan_to_num(b)
- except:
- edge_verts = np.array(edge_verts)
- arr = np.arange(n_edges)*2
- id0 = edge_verts[arr] # first vertex indices for each edge
- id1 = edge_verts[arr+1] # second vertex indices for each edge
- for i in range(time_steps):
- lap_a = np.zeros(n_verts)
- lap_b = np.zeros(n_verts)
- lap_a0 = a[id1] - a[id0] # laplacian increment for first vertex of each edge
- lap_b0 = b[id1] - b[id0] # laplacian increment for first vertex of each edge
-
- for i, j, la0, lb0 in np.nditer([id0,id1,lap_a0,lap_b0]):
- lap_a[i] += la0
- lap_b[i] += lb0
- lap_a[j] -= la0
- lap_b[j] -= lb0
- ab2 = a*b**2
- a += eval("(diff_a*lap_a - ab2 + f*(1-a))*dt")
- b += eval("(diff_b*lap_b + ab2 - (k+f)*b)*dt")
- #a += (diff_a*lap_a - ab2 + f*(1-a))*dt
- #b += (diff_b*lap_b + ab2 - (k+f)*b)*dt
-
- a = nan_to_num(a)
- b = nan_to_num(b)
-
- timeElapsed = time.time() - start
- print('RD - Simulation Time:',timeElapsed)
- start = time.time()
-
- for i in range(n_verts):
- ob.vertex_groups['A'].add([i], a[i], 'REPLACE')
- ob.vertex_groups['B'].add([i], b[i], 'REPLACE')
-
- for ps in ob.particle_systems:
- if ps.vertex_group_density == 'B' or ps.vertex_group_density == 'A':
- ps.invert_vertex_group_density = not ps.invert_vertex_group_density
- ps.invert_vertex_group_density = not ps.invert_vertex_group_density
-
- timeElapsed = time.time() - start
- print('RD - Closing Time:',timeElapsed)
-
-class TISSUE_PT_reaction_diffusion(Panel):
- bl_space_type = 'PROPERTIES'
- bl_region_type = 'WINDOW'
- bl_context = "data"
- bl_label = "Tissue - Reaction-Diffusion"
- bl_options = {'DEFAULT_CLOSED'}
-
- @classmethod
- def poll(cls, context):
- return 'A' and 'B' in context.object.vertex_groups
-
- def draw(self, context):
- reaction_diffusion_add_handler(self, context)
-
- ob = context.object
- props = ob.reaction_diffusion_settings
- layout = self.layout
- col = layout.column(align=True)
- row = col.row(align=True)
- if not ("A" and "B" in ob.vertex_groups):
- row.operator("object.start_reaction_diffusion",
- icon="EXPERIMENTAL",
- text="Reaction-Diffusion")
- else:
- row.operator("object.start_reaction_diffusion",
- icon="EXPERIMENTAL",
- text="Reset Reaction-Diffusion")
- row = col.row(align=True)
- row.prop(props, "run", text="Run Reaction-Diffusion")
- col = layout.column(align=True)
- row = col.row(align=True)
- row.prop(props, "time_steps")
- row.prop(props, "dt")
- col.separator()
- row = col.row(align=True)
- row.prop(props, "diff_a")
- row.prop(props, "diff_b")
- row = col.row(align=True)
- row.prop(props, "diff_mult")
- #col.separator()
- row = col.row(align=True)
- row.prop(props, "f")
- row.prop(props, "k")