Fix T53579: Move the settings into a Tools Panel, use wm property group

Bump version to 1.1.4
Remove some dead code
Use register class instead of register_module

Adress the F6 redo menu Blender crash issue by:
1) Moving the properties from the operator to a property group
of window manager type (bpy.types.WindowManager.ivy_gen_props)
2) Only the update is exposed in the operator undo (a boolprop)
3) Settings moved into the CURVE_PT_IvyGenPanel located under the
Tools Region > Create > Ivy Generator

Functionality should be the same as before:

Since the window manager props are not jumping back on operator redo
the operator just pulls them in execute which allows change in the Panel
while operator is still active

The default values are assigned by setting the operator defaultIvy to True
This commit is contained in:
Vuk Gardašević 2018-01-06 07:23:58 +01:00
parent 81d846baa2
commit ea4245645c
Notes: blender-bot 2023-02-14 19:33:12 +01:00
Referenced by issue #60729, Vine add on not showing up once I check the box to enable.
Referenced by issue #53684, 2.79a release - addons
Referenced by issue #53579, Ivy Generator crash
1 changed files with 220 additions and 178 deletions

View File

@ -21,9 +21,9 @@
bl_info = {
"name": "IvyGen",
"author": "testscreenings, PKHG, TrumanBlending",
"version": (0, 1, 2),
"version": (0, 1, 4),
"blender": (2, 59, 0),
"location": "View3D > Add > Curve",
"location": "View3D > Tool Shelf > Create > Ivy Generator",
"description": "Adds generated ivy to a mesh object starting "
"at the 3D cursor",
"warning": "",
@ -34,10 +34,16 @@ bl_info = {
import bpy
from bpy.types import (
Operator,
Panel,
PropertyGroup,
)
from bpy.props import (
BoolProperty,
FloatProperty,
IntProperty,
BoolProperty,
PointerProperty,
)
from mathutils import (
Vector,
@ -191,7 +197,7 @@ def createIvyGeometry(IVY, growLeaves):
if growLeaves:
faceList = [[4 * i + l for l in range(4)] for i in
range(len(vertList) // 4)]
range(len(vertList) // 4)]
# Generate the new leaf mesh and link
me = bpy.data.meshes.new('IvyLeaf')
@ -212,24 +218,6 @@ def createIvyGeometry(IVY, growLeaves):
ob.parent = newCurve
'''
def computeBoundingSphere(ob):
# Get the mesh data
me = ob.data
# Intialise the center
center = Vector((0.0, 0.0, 0.0))
# Add all vertex coords
for v in me.vertices:
center += v.co
# Average over all verts
center /= len(me.vertices)
# Create the iterator and find its max
length_iter = ((center - v.co).length for v in me.vertices)
radius = max(length_iter)
return radius
'''
class IvyNode:
""" The basic class used for each point on the ivy which is grown."""
__slots__ = ('pos', 'primaryDir', 'adhesionVector', 'adhesionLength',
@ -463,12 +451,204 @@ def check_mesh_faces(ob):
return False
class IvyGen(bpy.types.Operator):
class IvyGen(Operator):
bl_idname = "curve.ivy_gen"
bl_label = "IvyGen"
bl_description = "Generate Ivy on an Mesh Object"
bl_options = {'REGISTER', 'UNDO'}
updateIvy = BoolProperty(
name="Update Ivy",
description="Update the Ivy location based on the cursor and Panel settings",
default=False
)
defaultIvy = BoolProperty(
name="Default Ivy",
options={"HIDDEN", "SKIP_SAVE"},
default=False
)
@classmethod
def poll(self, context):
# Check if there's an object and whether it's a mesh
ob = context.active_object
return ((ob is not None) and
(ob.type == 'MESH') and
(context.mode == 'OBJECT'))
def invoke(self, context, event):
self.updateIvy = True
return self.execute(context)
def execute(self, context):
# scene = context.scene
ivyProps = context.window_manager.ivy_gen_props
if not self.updateIvy:
return {'PASS_THROUGH'}
# assign the variables, check if it is default
# Note: update the values if window_manager props defaults are changed
randomSeed = ivyProps.randomSeed if not self.defaultIvy else 0
maxTime = ivyProps.maxTime if not self.defaultIvy else 0
maxIvyLength = ivyProps.maxIvyLength if not self.defaultIvy else 1.0
ivySize = ivyProps.ivySize if not self.defaultIvy else 0.02
maxFloatLength = ivyProps.maxFloatLength if not self.defaultIvy else 0.5
maxAdhesionDistance = ivyProps.maxAdhesionDistance if not self.defaultIvy else 1.0
primaryWeight = ivyProps.primaryWeight if not self.defaultIvy else 0.5
randomWeight = ivyProps.randomWeight if not self.defaultIvy else 0.2
gravityWeight = ivyProps.gravityWeight if not self.defaultIvy else 1.0
adhesionWeight = ivyProps.adhesionWeight if not self.defaultIvy else 0.1
branchingProbability = ivyProps.branchingProbability if not self.defaultIvy else 0.05
leafProbability = ivyProps.leafProbability if not self.defaultIvy else 0.35
ivyBranchSize = ivyProps.ivyBranchSize if not self.defaultIvy else 0.001
ivyLeafSize = ivyProps.ivyLeafSize if not self.defaultIvy else 0.02
growLeaves = ivyProps.growLeaves if not self.defaultIvy else True
bpy.ops.object.mode_set(mode='EDIT', toggle=False)
bpy.ops.object.mode_set(mode='OBJECT', toggle=False)
# Get the selected object
ob = context.active_object
# Check if the mesh has at least one polygon since some functions
# are expecting them in the object's data (see T51753)
check_face = check_mesh_faces(ob)
if check_face is False:
self.report({'WARNING'},
"Mesh Object doesn't have at least one Face. "
"Operation Cancelled")
return {"CANCELLED"}
# Compute bounding sphere radius
# radius = computeBoundingSphere(ob) # Not needed anymore
# Get the seeding point
seedPoint = context.scene.cursor_location
# Fix the random seed
rand_seed(randomSeed)
# Make the new ivy
IVY = Ivy(
primaryWeight=primaryWeight,
randomWeight=randomWeight,
gravityWeight=gravityWeight,
adhesionWeight=adhesionWeight,
branchingProbability=branchingProbability,
leafProbability=leafProbability,
ivySize=ivySize,
ivyLeafSize=ivyLeafSize,
ivyBranchSize=ivyBranchSize,
maxFloatLength=maxFloatLength,
maxAdhesionDistance=maxAdhesionDistance
)
# Generate first root and node
IVY.seed(seedPoint)
checkTime = False
maxLength = maxIvyLength # * radius
# If we need to check time set the flag
if maxTime != 0.0:
checkTime = True
t = time.time()
startPercent = 0.0
checkAliveIter = [True, ]
# Grow until 200 roots is reached or backup counter exceeds limit
while (any(checkAliveIter) and
(IVY.maxLength < maxLength) and
(not checkTime or (time.time() - t < maxTime))):
# Grow the ivy for this iteration
IVY.grow(ob)
# Print the proportion of ivy growth to console
if (IVY.maxLength / maxLength * 100) > 10 * startPercent // 10:
print('%0.2f%% of Ivy nodes have grown' %
(IVY.maxLength / maxLength * 100))
startPercent += 10
if IVY.maxLength / maxLength > 1:
print("Halting Growth")
# Make an iterator to check if all are alive
checkAliveIter = (r.alive for r in IVY.ivyRoots)
# Create the curve and leaf geometry
createIvyGeometry(IVY, growLeaves)
print("Geometry Generation Complete")
print("Ivy generated in %0.2f s" % (time.time() - t))
self.updateIvy = False
self.defaultIvy = False
return {'FINISHED'}
def draw(self, context):
layout = self.layout
layout.prop(self, "updateIvy", icon="FILE_REFRESH")
class CURVE_PT_IvyGenPanel(Panel):
bl_label = "Ivy Generator"
bl_idname = "CURVE_PT_IvyGenPanel"
bl_space_type = "VIEW_3D"
bl_region_type = "TOOLS"
bl_category = "Create"
bl_context = 'objectmode'
bl_options = {"DEFAULT_CLOSED"}
def draw(self, context):
layout = self.layout
wm = context.window_manager
col = layout.column(align=True)
prop_new = col.operator("curve.ivy_gen", text="Add New Ivy", icon="OUTLINER_OB_CURVE")
prop_new.defaultIvy = False
prop_new.updateIvy = True
prop_def = col.operator("curve.ivy_gen", text="Add New Default Ivy", icon="CURVE_DATA")
prop_def.defaultIvy = True
prop_def.updateIvy = True
col = layout.column(align=True)
col.label("Generation Settings:")
col.prop(wm.ivy_gen_props, "randomSeed")
col.prop(wm.ivy_gen_props, "maxTime")
col = layout.column(align=True)
col.label("Size Settings:")
col.prop(wm.ivy_gen_props, "maxIvyLength")
col.prop(wm.ivy_gen_props, "ivySize")
col.prop(wm.ivy_gen_props, "maxFloatLength")
col.prop(wm.ivy_gen_props, "maxAdhesionDistance")
col = layout.column(align=True)
col.label("Weight Settings:")
col.prop(wm.ivy_gen_props, "primaryWeight")
col.prop(wm.ivy_gen_props, "randomWeight")
col.prop(wm.ivy_gen_props, "gravityWeight")
col.prop(wm.ivy_gen_props, "adhesionWeight")
col = layout.column(align=True)
col.label("Branch Settings:")
col.prop(wm.ivy_gen_props, "branchingProbability")
col.prop(wm.ivy_gen_props, "ivyBranchSize")
col = layout.column(align=True)
col.prop(wm.ivy_gen_props, "growLeaves")
if wm.ivy_gen_props.growLeaves:
col = layout.column(align=True)
col.label("Leaf Settings:")
col.prop(wm.ivy_gen_props, "ivyLeafSize")
col.prop(wm.ivy_gen_props, "leafProbability")
class IvyGenProperties(PropertyGroup):
maxIvyLength = FloatProperty(
name="Max Ivy Length",
description="Maximum ivy length in Blender Units",
@ -551,7 +731,8 @@ class IvyGen(bpy.types.Operator):
"can live while floating",
default=0.5,
min=0.0,
soft_max=1.0)
soft_max=1.0
)
maxAdhesionDistance = FloatProperty(
name="Max Adhesion Length",
description="The maximum distance that a branch "
@ -580,168 +761,29 @@ class IvyGen(bpy.types.Operator):
description="Grow leaves or not",
default=True
)
updateIvy = BoolProperty(
name="Update Ivy",
default=False
)
@classmethod
def poll(self, context):
# Check if there's an object and whether it's a mesh
ob = context.active_object
return ((ob is not None) and
(ob.type == 'MESH') and
(context.mode == 'OBJECT'))
def invoke(self, context, event):
self.updateIvy = True
return self.execute(context)
def execute(self, context):
if not self.updateIvy:
return {'PASS_THROUGH'}
bpy.ops.object.mode_set(mode='EDIT', toggle=False)
bpy.ops.object.mode_set(mode='OBJECT', toggle=False)
# Get the selected object
ob = context.active_object
# Check if the mesh has at least one polygon since some functions
# are expecting them in the object's data (see T51753)
check_face = check_mesh_faces(ob)
if check_face is False:
self.report({'WARNING'},
"Mesh Object doesn't have at least one Face. "
"Operation Cancelled")
return {"CANCELLED"}
# Compute bounding sphere radius
# radius = computeBoundingSphere(ob) # Not needed anymore
# Get the seeding point
seedPoint = context.scene.cursor_location
# Fix the random seed
rand_seed(self.randomSeed)
# Make the new ivy
IVY = Ivy(**self.as_keywords(ignore=('randomSeed', 'growLeaves',
'maxIvyLength', 'maxTime', 'updateIvy')))
# Generate first root and node
IVY.seed(seedPoint)
checkTime = False
maxLength = self.maxIvyLength # * radius
# If we need to check time set the flag
if self.maxTime != 0.0:
checkTime = True
t = time.time()
startPercent = 0.0
checkAliveIter = [True, ]
# Grow until 200 roots is reached or backup counter exceeds limit
while (any(checkAliveIter) and
(IVY.maxLength < maxLength) and
(not checkTime or (time.time() - t < self.maxTime))):
# Grow the ivy for this iteration
IVY.grow(ob)
# Print the proportion of ivy growth to console
if (IVY.maxLength / maxLength * 100) > 10 * startPercent // 10:
print('%0.2f%% of Ivy nodes have grown' %
(IVY.maxLength / maxLength * 100))
startPercent += 10
if IVY.maxLength / maxLength > 1:
print("Halting Growth")
# Make an iterator to check if all are alive
checkAliveIter = (r.alive for r in IVY.ivyRoots)
# Create the curve and leaf geometry
createIvyGeometry(IVY, self.growLeaves)
print("Geometry Generation Complete")
print("Ivy generated in %0.2f s" % (time.time() - t))
self.updateIvy = False
return {'FINISHED'}
def draw(self, context):
layout = self.layout
layout.prop(self, 'updateIvy', icon='CURVE_DATA')
properties = layout.operator('curve.ivy_gen', text="Add New Ivy")
properties.randomSeed = self.randomSeed
properties.maxTime = self.maxTime
properties.maxIvyLength = self.maxIvyLength
properties.ivySize = self.ivySize
properties.maxFloatLength = self.maxFloatLength
properties.maxAdhesionDistance = self.maxAdhesionDistance
properties.primaryWeight = self.primaryWeight
properties.randomWeight = self.randomWeight
properties.gravityWeight = self.gravityWeight
properties.adhesionWeight = self.adhesionWeight
properties.branchingProbability = self.branchingProbability
properties.leafProbability = self.leafProbability
properties.ivyBranchSize = self.ivyBranchSize
properties.ivyLeafSize = self.ivyLeafSize
properties.updateIvy = True
prop_def = layout.operator('curve.ivy_gen', text="Add New Default Ivy")
prop_def.updateIvy = True
layout.prop(self, 'growLeaves')
box = layout.box()
box.label("Generation Settings:")
box.prop(self, 'randomSeed')
box.prop(self, 'maxTime')
box = layout.box()
box.label("Size Settings:")
box.prop(self, 'maxIvyLength')
box.prop(self, 'ivySize')
box.prop(self, 'maxFloatLength')
box.prop(self, 'maxAdhesionDistance')
box = layout.box()
box.label("Weight Settings:")
box.prop(self, 'primaryWeight')
box.prop(self, 'randomWeight')
box.prop(self, 'gravityWeight')
box.prop(self, 'adhesionWeight')
box = layout.box()
box.label("Branch Settings:")
box.prop(self, 'branchingProbability')
box.prop(self, 'ivyBranchSize')
if self.growLeaves:
box = layout.box()
box.label("Leaf Settings:")
box.prop(self, 'ivyLeafSize')
box.prop(self, 'leafProbability')
def menu_func(self, context):
self.layout.operator(IvyGen.bl_idname, text="Add Ivy to Mesh",
icon='OUTLINER_DATA_CURVE').updateIvy = True
classes = (
IvyGen,
IvyGenProperties,
CURVE_PT_IvyGenPanel
)
def register():
bpy.utils.register_module(__name__)
bpy.types.INFO_MT_curve_add.append(menu_func)
for cls in classes:
bpy.utils.register_class(cls)
bpy.types.WindowManager.ivy_gen_props = PointerProperty(
type=IvyGenProperties
)
def unregister():
bpy.types.INFO_MT_curve_add.remove(menu_func)
bpy.utils.unregister_module(__name__)
del bpy.types.WindowManager.ivy_gen_props
for cls in reversed(classes):
bpy.utils.unregister_class(cls)
if __name__ == "__main__":