Skip to content
Snippets Groups Projects
__init__.py 36.7 KiB
Newer Older
  • Learn to ignore specific revisions
  • # -*- coding: utf-8 -*-
    
    # ##### BEGIN GPL LICENSE BLOCK #####
    
    Andrew Hale's avatar
    Andrew Hale committed
    #
    #  This program is free software; you can redistribute it and/or
    #  modify it under the terms of the GNU General Public License
    #  as published by the Free Software Foundation; either version 2
    #  of the License, or (at your option) any later version.
    #
    #  This program is distributed in the hope that it will be useful,
    #  but WITHOUT ANY WARRANTY; without even the implied warranty of
    #  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    #  GNU General Public License for more details.
    #
    #  You should have received a copy of the GNU General Public License
    #  along with this program; if not, write to the Free Software Foundation,
    #  Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
    #
    
    # ##### END GPL LICENSE BLOCK #####
    
    Andrew Hale's avatar
    Andrew Hale committed
    
    bl_info = {
    
        "name": "Sapling Tree Gen",
    
        "author": "Andrew Hale (TrumanBlending), Aaron Buchler, CansecoGPC",
        "version": (0, 3, 4),
        "blender": (2, 80, 0),
    
    Andrew Hale's avatar
    Andrew Hale committed
        "location": "View3D > Add > Curve",
        "description": ("Adds a parametric tree. The method is presented by "
        "Jason Weber & Joseph Penn in their paper 'Creation and Rendering of "
    
        "Realistic Trees'"),
    
        "doc_url": "{BLENDER_MANUAL_URL}/addons/add_curve/sapling.html",
    
    Andrew Hale's avatar
    Andrew Hale committed
    if "bpy" in locals():
    
        import importlib
        importlib.reload(utils)
    
    Andrew Hale's avatar
    Andrew Hale committed
    else:
        from add_curve_sapling import utils
    
    import bpy
    import time
    import os
    
    # import cProfile
    
    from bpy.types import (
            Operator,
            Menu,
            )
    from bpy.props import (
            BoolProperty,
            EnumProperty,
            FloatProperty,
            FloatVectorProperty,
            IntProperty,
            IntVectorProperty,
            StringProperty,
            )
    
    Andrew Hale's avatar
    Andrew Hale committed
    
    useSet = False
    
    shapeList = [('0', 'Conical (0)', 'Shape = 0'),
                ('1', 'Spherical (1)', 'Shape = 1'),
                ('2', 'Hemispherical (2)', 'Shape = 2'),
                ('3', 'Cylindrical (3)', 'Shape = 3'),
                ('4', 'Tapered Cylindrical (4)', 'Shape = 4'),
                ('5', 'Flame (5)', 'Shape = 5'),
                ('6', 'Inverse Conical (6)', 'Shape = 6'),
                ('7', 'Tend Flame (7)', 'Shape = 7')]
    
    
    shapeList3 = [('0', 'Conical', ''),
                ('6', 'Inverse Conical', ''),
                ('1', 'Spherical', ''),
                ('2', 'Hemispherical', ''),
                ('3', 'Cylindrical', ''),
                ('4', 'Tapered Cylindrical', ''),
                ('10', 'Inverse Tapered Cylindrical', ''),
                ('5', 'Flame', ''),
                ('7', 'Tend Flame', ''),
                ('8', 'Custom Shape', '')]
    
    shapeList4 = [('0', 'Conical', ''),
                ('6', 'Inverse Conical', ''),
                ('1', 'Spherical', ''),
                ('2', 'Hemispherical', ''),
                ('3', 'Cylindrical', ''),
                ('4', 'Tapered Cylindrical', ''),
                ('10', 'Inverse Tapered Cylindrical', ''),
                ('5', 'Flame', ''),
                ('7', 'Tend Flame', '')]
    
    
    Andrew Hale's avatar
    Andrew Hale committed
    handleList = [('0', 'Auto', 'Auto'),
                    ('1', 'Vector', 'Vector')]
    
    settings = [('0', 'Geometry', 'Geometry'),
    
                ('1', 'Branch Radius', 'Branch Radius'),
                ('2', 'Branch Splitting', 'Branch Splitting'),
                ('3', 'Branch Growth', 'Branch Growth'),
                ('4', 'Pruning', 'Pruning'),
                ('5', 'Leaves', 'Leaves'),
                ('6', 'Armature', 'Armature'),
                ('7', 'Animation', 'Animation')]
    
    branchmodes = [("original", "Original", "rotate around each branch"),
                  ("rotate", "Rotate", "evenly distribute  branches to point outward from center of tree"),
                  ("random", "Random", "choose random point")]
    
    Andrew Hale's avatar
    Andrew Hale committed
    def getPresetpath():
    
        """Support user defined scripts directory
    
           Find the first occurrence of add_curve_sapling/presets in possible script paths
    
           and return it as preset path"""
    
        script_file = os.path.realpath(__file__)
        directory = os.path.dirname(script_file)
        directory = os.path.join(directory, "presets")
        return directory
    
    
    def getPresetpaths():
        """Return paths for both local and user preset folders"""
        userDir = os.path.join(bpy.utils.script_path_user(), 'presets', 'operator', 'add_curve_sapling')
    
        if os.path.isdir(userDir):
            pass
        else:
            os.makedirs(userDir)
    
        script_file = os.path.realpath(__file__)
        directory = os.path.dirname(script_file)
        localDir = os.path.join(directory, "presets")
    
        return (localDir, userDir)
    
    
    class ExportData(Operator):
    
        """This operator handles writing presets to file"""
    
    Andrew Hale's avatar
    Andrew Hale committed
        bl_idname = 'sapling.exportdata'
        bl_label = 'Export Preset'
    
    
        data: StringProperty()
    
    Andrew Hale's avatar
    Andrew Hale committed
    
        def execute(self, context):
            # Unpack some data from the input
    
            data, filename, overwrite = eval(self.data)
    
            """
            try:
                # Check whether the file exists by trying to open it.
                f = open(os.path.join(getPresetpaths()[1], filename + '.py'), 'r')
                f.close()
                # If it exists then report an error
                self.report({'ERROR_INVALID_INPUT'}, 'Preset Already Exists')
                return {'CANCELLED'}
            except IOError:
                if data:
                   # If it doesn't exist, create the file with the required data
                   f = open(os.path.join(getPresetpaths()[1], filename + '.py'), 'w')
                   f.write(data)
                   f.close()
                   return {'FINISHED'}
            else:
                return {'CANCELLED'}
            """
    
            fpath1 = os.path.join(getPresetpaths()[0], filename + '.py')
            fpath2 = os.path.join(getPresetpaths()[1], filename + '.py')
    
            if os.path.exists(fpath1):
                # If it exists in built-in presets then report an error
                self.report({'ERROR_INVALID_INPUT'}, 'Can\'t have same name as built-in preset')
    
    Andrew Hale's avatar
    Andrew Hale committed
                return {'CANCELLED'}
    
            elif (not os.path.exists(fpath2)) or (os.path.exists(fpath2) and overwrite):
    
                # if (it does not exist) or (exists and overwrite) then write file
    
    Andrew Hale's avatar
    Andrew Hale committed
                if data:
                    # If it doesn't exist, create the file with the required data
    
                    f = open(os.path.join(getPresetpaths()[1], filename + '.py'), 'w')
    
    Andrew Hale's avatar
    Andrew Hale committed
                    f.write(data)
                    f.close()
                    return {'FINISHED'}
                else:
                    return {'CANCELLED'}
    
            else:
                # If it exists then report an error
                self.report({'ERROR_INVALID_INPUT'}, 'Preset Already Exists')
                return {'CANCELLED'}
    
    
    class ImportData(Operator):
    
        """This operator handles importing existing presets"""
    
        bl_idname = "sapling.importdata"
        bl_label = "Import Preset"
    
        filename: StringProperty()
    
    Andrew Hale's avatar
    Andrew Hale committed
    
        def execute(self, context):
            # Make sure the operator knows about the global variables
            global settings, useSet
            # Read the preset data into the global settings
    
            try:
                f = open(os.path.join(getPresetpaths()[0], self.filename), 'r')
            except (FileNotFoundError, IOError):
                f = open(os.path.join(getPresetpaths()[1], self.filename), 'r')
    
    Andrew Hale's avatar
    Andrew Hale committed
            settings = f.readline()
            f.close()
    
            # print(settings)
    
            settings = ast.literal_eval(settings)
    
    
            # use old attractup
    
            if type(settings['attractUp']) == float:
                atr = settings['attractUp']
                settings['attractUp'] = [0, 0, atr, atr]
    
    
            # use old leaf rotations
    
            if 'leafDownAngle' not in settings:
                l = settings['levels']
                settings['leafDownAngle'] = settings['downAngle'][min(l, 3)]
                settings['leafDownAngleV'] = settings['downAngleV'][min(l, 3)]
                settings['leafRotate'] = settings['rotate'][min(l, 3)]
                settings['leafRotateV'] = settings['rotateV'][min(l, 3)]
    
    
            # zero leaf bend
    
    Andrew Hale's avatar
    Andrew Hale committed
            # Set the flag to use the settings
            useSet = True
            return {'FINISHED'}
    
    
    
    class PresetMenu(Menu):
    
        """Create the preset menu by finding all preset files
        in the preset directory"""
    
        bl_idname = "SAPLING_MT_preset"
    
    Andrew Hale's avatar
    Andrew Hale committed
        bl_label = "Presets"
    
        def draw(self, context):
            # Get all the sapling presets
    
            presets = [a for a in os.listdir(getPresetpaths()[0]) if a[-3:] == '.py']
            presets.extend([a for a in os.listdir(getPresetpaths()[1]) if a[-3:] == '.py'])
    
    Andrew Hale's avatar
    Andrew Hale committed
            layout = self.layout
            # Append all to the menu
            for p in presets:
                layout.operator("sapling.importdata", text=p[:-3]).filename = p
    
    
    
    class AddTree(Operator):
    
    Andrew Hale's avatar
    Andrew Hale committed
        bl_idname = "curve.tree_add"
    
    Andrew Hale's avatar
    Andrew Hale committed
        bl_options = {'REGISTER', 'UNDO'}
    
    
        def objectList(self, context):
            objects = []
            bObjects = bpy.data.objects
    
            for obj in bObjects:
                if (obj.type in ['MESH', 'CURVE', 'SURFACE']) and (obj.name not in ['tree', 'leaves']):
                    objects.append((obj.name, obj.name, ""))
    
    
            return (objects if objects else
                    [('NONE', "No objects", "No appropriate objects in the Scene")])
    
    Andrew Hale's avatar
    Andrew Hale committed
    
        def update_tree(self, context):
            self.do_update = True
    
        def update_leaves(self, context):
            if self.showLeaves:
                self.do_update = True
            else:
                self.do_update = False
    
    
    Andrew Hale's avatar
    Andrew Hale committed
        def no_update_tree(self, context):
            self.do_update = False
    
    
        do_update: BoolProperty(
    
            name='Do Update',
            default=True, options={'HIDDEN'}
            )
    
        chooseSet: EnumProperty(
    
            name='Settings',
    
    Andrew Hale's avatar
    Andrew Hale committed
            description='Choose the settings to modify',
            items=settings,
    
            default='0', update=no_update_tree
            )
    
        bevel: BoolProperty(
    
            name='Bevel',
    
    Bastien Montagne's avatar
    Bastien Montagne committed
            description='Whether the curve is beveled',
    
            default=False, update=update_tree
            )
    
        prune: BoolProperty(
    
            name='Prune',
    
    Andrew Hale's avatar
    Andrew Hale committed
            description='Whether the tree is pruned',
    
            default=False, update=update_tree
            )
    
        showLeaves: BoolProperty(
    
            name='Show Leaves',
    
    Andrew Hale's avatar
    Andrew Hale committed
            description='Whether the leaves are shown',
    
            default=False, update=update_tree
            )
    
        useArm: BoolProperty(
    
            name='Use Armature',
    
    Andrew Hale's avatar
    Andrew Hale committed
            description='Whether the armature is generated',
    
            default=False, update=update_tree
            )
    
        seed: IntProperty(
    
            name='Random Seed',
    
    Andrew Hale's avatar
    Andrew Hale committed
            description='The seed of the random number generator',
    
            default=0, update=update_tree
            )
    
        handleType: IntProperty(
    
            name='Handle Type',
    
    Andrew Hale's avatar
    Andrew Hale committed
            description='The type of curve handles',
            min=0,
            max=1,
    
            default=0, update=update_tree
            )
    
        levels: IntProperty(
    
            name='Levels',
    
            description='Number of recursive branches (Levels)',
    
    Andrew Hale's avatar
    Andrew Hale committed
            min=1,
            max=6,
    
            default=3, update=update_tree
            )
    
        length: FloatVectorProperty(
    
            name='Length',
    
    Andrew Hale's avatar
    Andrew Hale committed
            description='The relative lengths of each branch level (nLength)',
    
    Andrew Hale's avatar
    Andrew Hale committed
            default=[1, 0.3, 0.6, 0.45],
    
            size=4, update=update_tree
            )
    
        lengthV: FloatVectorProperty(
    
            name='Length Variation',
    
    Andrew Hale's avatar
    Andrew Hale committed
            description='The relative length variations of each level (nLengthV)',
            min=0.0,
    
    Andrew Hale's avatar
    Andrew Hale committed
            default=[0, 0, 0, 0],
    
            size=4, update=update_tree
            )
    
        taperCrown: FloatProperty(
    
            name='Taper Crown',
    
            description='Shorten trunk splits toward outside of tree',
            min=0.0,
            soft_max=1.0,
    
            default=0, update=update_tree
            )
    
        branches: IntVectorProperty(
    
            name='Branches',
    
    Andrew Hale's avatar
    Andrew Hale committed
            description='The number of branches grown at each level (nBranches)',
            min=0,
            default=[50, 30, 10, 10],
    
            size=4, update=update_tree
            )
    
        curveRes: IntVectorProperty(
    
            name='Curve Resolution',
    
    Andrew Hale's avatar
    Andrew Hale committed
            description='The number of segments on each branch (nCurveRes)',
            min=1,
            default=[3, 5, 3, 1],
    
            size=4, update=update_tree
            )
    
        curve: FloatVectorProperty(
    
            name='Curvature',
    
    Andrew Hale's avatar
    Andrew Hale committed
            description='The angle of the end of the branch (nCurve)',
            default=[0, -40, -40, 0],
    
            size=4, update=update_tree
            )
    
        curveV: FloatVectorProperty(
    
            name='Curvature Variation',
    
    Andrew Hale's avatar
    Andrew Hale committed
            description='Variation of the curvature (nCurveV)',
            default=[20, 50, 75, 0],
    
            size=4, update=update_tree
            )
    
        curveBack: FloatVectorProperty(
    
            name='Back Curvature',
    
    Andrew Hale's avatar
    Andrew Hale committed
            description='Curvature for the second half of a branch (nCurveBack)',
            default=[0, 0, 0, 0],
    
            size=4, update=update_tree
            )
    
        baseSplits: IntProperty(
    
            name='Base Splits',
    
    Andrew Hale's avatar
    Andrew Hale committed
            description='Number of trunk splits at its base (nBaseSplits)',
            min=0,
    
            default=0, update=update_tree
            )
    
        segSplits: FloatVectorProperty(
    
            name='Segment Splits',
    
    Andrew Hale's avatar
    Andrew Hale committed
            description='Number of splits per segment (nSegSplits)',
            min=0,
    
    Andrew Hale's avatar
    Andrew Hale committed
            default=[0, 0, 0, 0],
    
            size=4, update=update_tree
            )
    
        splitByLen: BoolProperty(
    
            name='Split relative to length',
    
            description='Split proportional to branch length',
    
            default=False, update=update_tree
            )
    
        rMode: EnumProperty(
    
            name="",  # "Branching Mode"
    
            description='Branching and Rotation Mode',
            items=branchmodes,
    
            default="rotate", update=update_tree
            )
    
        splitAngle: FloatVectorProperty(
    
            name='Split Angle',
    
    Andrew Hale's avatar
    Andrew Hale committed
            description='Angle of branch splitting (nSplitAngle)',
            default=[0, 0, 0, 0],
    
            size=4, update=update_tree
            )
    
        splitAngleV: FloatVectorProperty(
    
            name='Split Angle Variation',
    
    Andrew Hale's avatar
    Andrew Hale committed
            description='Variation in the split angle (nSplitAngleV)',
            default=[0, 0, 0, 0],
    
            size=4, update=update_tree
            )
    
        scale: FloatProperty(
    
            name='Scale',
    
    Andrew Hale's avatar
    Andrew Hale committed
            description='The tree scale (Scale)',
    
    Andrew Hale's avatar
    Andrew Hale committed
            default=13.0, update=update_tree)
    
        scaleV: FloatProperty(name='Scale Variation',
    
    Andrew Hale's avatar
    Andrew Hale committed
            description='The variation in the tree scale (ScaleV)',
    
            default=3.0, update=update_tree
            )
    
        attractUp: FloatVectorProperty(
    
            name='Vertical Attraction',
    
    Andrew Hale's avatar
    Andrew Hale committed
            description='Branch upward attraction',
    
            default=[0, 0, 0, 0],
    
            size=4, update=update_tree
            )
    
        attractOut: FloatVectorProperty(
    
            name='Outward Attraction',
    
            description='Branch outward attraction',
            default=[0, 0, 0, 0],
            min=0.0,
            max=1.0,
    
            size=4, update=update_tree
            )
    
        shape: EnumProperty(
    
            name='Shape',
    
            description='The overall shape of the tree (Shape)',
            items=shapeList3,
    
            default='7', update=update_tree
            )
    
        shapeS: EnumProperty(
    
            name='Secondary Branches Shape',
    
            description='The shape of secondary splits',
            items=shapeList4,
    
            default='4', update=update_tree
            )
    
        customShape: FloatVectorProperty(
    
            name='Custom Shape',
    
            description='custom shape branch length at (Base, Middle, Middle Position, Top)',
            size=4,
            min=.01,
            max=1,
    
            default=[.5, 1.0, .3, .5], update=update_tree
            )
    
        branchDist: FloatProperty(
    
            name='Branch Distribution',
    
            description='Adjust branch spacing to put more branches at the top or bottom of the tree',
            min=0.1,
            soft_max=10,
    
            default=1.0, update=update_tree
            )
    
        nrings: IntProperty(
    
            name='Branch Rings',
    
            description='grow branches in rings',
            min=0,
    
            default=0, update=update_tree
            )
    
        baseSize: FloatProperty(
    
            name='Trunk Height',
    
            description='Fraction of tree height with no branches (Base Size)',
            min=0.0,
    
    Andrew Hale's avatar
    Andrew Hale committed
            max=1.0,
    
            default=0.4, update=update_tree
            )
    
        baseSize_s: FloatProperty(
    
            name='Secondary Base Size',
    
            description='Factor to decrease base size for each level',
            min=0.0,
            max=1.0,
    
            default=0.25, update=update_tree
            )
    
        splitHeight: FloatProperty(
    
            name='Split Height',
    
            description='Fraction of tree height with no splits',
            min=0.0,
            max=1.0,
    
            default=0.2, update=update_tree
            )
    
        splitBias: FloatProperty(
    
            name='splitBias',
    
            description='Put more splits at the top or bottom of the tree',
            soft_min=-2.0,
            soft_max=2.0,
    
            default=0.0, update=update_tree
            )
    
        ratio: FloatProperty(
    
            name='Ratio',
    
    Andrew Hale's avatar
    Andrew Hale committed
            description='Base radius size (Ratio)',
            min=0.0,
    
            default=0.015, update=update_tree
            )
    
        minRadius: FloatProperty(
    
            name='Minimum Radius',
    
            description='Minimum branch Radius',
            min=0.0,
    
            default=0.0, update=update_tree
            )
    
        closeTip: BoolProperty(
    
            name='Close Tip',
    
            description='Set radius at branch tips to zero',
    
            default=False, update=update_tree
            )
    
        rootFlare: FloatProperty(
    
            name='Root Flare',
    
            description='Root radius factor',
            min=1.0,
    
            default=1.0, update=update_tree
            )
    
        autoTaper: BoolProperty(
    
            name='Auto Taper',
    
            description='Calculate taper automatically based on branch lengths',
    
            default=True, update=update_tree
            )
    
        taper: FloatVectorProperty(
    
            name='Taper',
    
    Andrew Hale's avatar
    Andrew Hale committed
            description='The fraction of tapering on each branch (nTaper)',
            min=0.0,
            max=1.0,
            default=[1, 1, 1, 1],
    
            size=4, update=update_tree
            )
    
        radiusTweak: FloatVectorProperty(
    
            name='Tweak Radius',
    
            description='multiply radius by this factor',
            min=0.0,
            max=1.0,
            default=[1, 1, 1, 1],
    
            size=4, update=update_tree
            )
    
        ratioPower: FloatProperty(
    
            name='Branch Radius Ratio',
    
    Andrew Hale's avatar
    Andrew Hale committed
            description=('Power which defines the radius of a branch compared to '
            'the radius of the branch it grew from (RatioPower)'),
            min=0.0,
    
            default=1.2, update=update_tree
            )
    
        downAngle: FloatVectorProperty(
    
            name='Down Angle',
    
    Andrew Hale's avatar
    Andrew Hale committed
            description=('The angle between a new branch and the one it grew '
            'from (nDownAngle)'),
            default=[90, 60, 45, 45],
    
            size=4, update=update_tree
            )
    
        downAngleV: FloatVectorProperty(
    
            name='Down Angle Variation',
            description="Angle to decrease Down Angle by towards end of parent branch "
                        "(negative values add random variation)",
    
    Andrew Hale's avatar
    Andrew Hale committed
            default=[0, -50, 10, 10],
    
            size=4, update=update_tree
            )
    
        useOldDownAngle: BoolProperty(
    
            name='Use old down angle variation',
            default=False, update=update_tree
            )
    
        useParentAngle: BoolProperty(
    
            name='Use parent angle',
            description='(first level) Rotate branch to match parent branch',
            default=True, update=update_tree
            )
    
        rotate: FloatVectorProperty(
    
            name='Rotate Angle',
            description="The angle of a new branch around the one it grew from "
                        "(negative values rotate opposite from the previous)",
    
            default=[137.5, 137.5, 137.5, 137.5],
    
            size=4, update=update_tree
            )
    
        rotateV: FloatVectorProperty(
    
            name='Rotate Angle Variation',
    
    Andrew Hale's avatar
    Andrew Hale committed
            description='Variation in the rotate angle (nRotateV)',
            default=[0, 0, 0, 0],
    
            size=4, update=update_tree
            )
    
        scale0: FloatProperty(
    
            name='Radius Scale',
    
    Andrew Hale's avatar
    Andrew Hale committed
            description='The scale of the trunk radius (0Scale)',
            min=0.0,
    
            default=1.0, update=update_tree
            )
    
        scaleV0: FloatProperty(
    
            name='Radius Scale Variation',
    
    Andrew Hale's avatar
    Andrew Hale committed
            description='Variation in the radius scale (0ScaleV)',
    
            default=0.2, update=update_tree
            )
    
        pruneWidth: FloatProperty(
    
            name='Prune Width',
    
    Andrew Hale's avatar
    Andrew Hale committed
            description='The width of the envelope (PruneWidth)',
            min=0.0,
    
            default=0.4, update=update_tree
            )
    
        pruneBase: FloatProperty(
    
            name='Prune Base Height',
    
            description='The height of the base of the envelope, bound by trunk height',
            min=0.0,
            max=1.0,
    
            default=0.3, update=update_tree
            )
    
        pruneWidthPeak: FloatProperty(
    
            name='Prune Width Peak',
            description=("Fraction of envelope height where the maximum width "
                         "occurs (PruneWidthPeak)"),
    
    Andrew Hale's avatar
    Andrew Hale committed
            min=0.0,
    
            default=0.6, update=update_tree
            )
    
        prunePowerHigh: FloatProperty(
    
            name='Prune Power High',
    
    Andrew Hale's avatar
    Andrew Hale committed
            description=('Power which determines the shape of the upper portion '
            'of the envelope (PrunePowerHigh)'),
    
            default=0.5, update=update_tree
            )
    
        prunePowerLow: FloatProperty(
    
            name='Prune Power Low',
    
    Andrew Hale's avatar
    Andrew Hale committed
            description=('Power which determines the shape of the lower portion '
            'of the envelope (PrunePowerLow)'),
    
            default=0.001, update=update_tree
            )
    
        pruneRatio: FloatProperty(
    
            name='Prune Ratio',
    
    Andrew Hale's avatar
    Andrew Hale committed
            description='Proportion of pruned length (PruneRatio)',
            min=0.0,
            max=1.0,
    
            default=1.0, update=update_tree
            )
    
        leaves: IntProperty(
    
            name='Leaves',
            description="Maximum number of leaves per branch (negative values grow "
                        "leaves from branch tip (palmate compound leaves))",
            default=25, update=update_tree
            )
    
        leafDownAngle: FloatProperty(
    
            name='Leaf Down Angle',
    
            description='The angle between a new leaf and the branch it grew from',
    
            default=45, update=update_leaves
            )
    
        leafDownAngleV: FloatProperty(
    
            name='Leaf Down Angle Variation',
            description="Angle to decrease Down Angle by towards end of parent branch "
                        "(negative values add random variation)",
            default=10, update=update_tree
            )
    
        leafRotate: FloatProperty(
    
            name='Leaf Rotate Angle',
            description="The angle of a new leaf around the one it grew from "
                        "(negative values rotate opposite from previous)",
            default=137.5, update=update_tree
            )
    
        leafRotateV: FloatProperty(
    
            name='Leaf Rotate Angle Variation',
    
            description='Variation in the rotate angle',
    
            default=0.0, update=update_leaves
            )
    
        leafScale: FloatProperty(
    
            name='Leaf Scale',
    
    Andrew Hale's avatar
    Andrew Hale committed
            description='The scaling applied to the whole leaf (LeafScale)',
            min=0.0,
    
            default=0.17, update=update_leaves
            )
    
        leafScaleX: FloatProperty(
    
            name='Leaf Scale X',
    
    Andrew Hale's avatar
    Andrew Hale committed
            description=('The scaling applied to the x direction of the leaf '
            '(LeafScaleX)'),
            min=0.0,
    
            default=1.0, update=update_leaves
            )
    
        leafScaleT: FloatProperty(
    
            name='Leaf Scale Taper',
    
            description='scale leaves toward the tip or base of the patent branch',
    
            min=-1.0,
            max=1.0,
            default=0.0, update=update_leaves
            )
    
        leafScaleV: FloatProperty(
    
            name='Leaf Scale Variation',
    
            description='randomize leaf scale',
    
            min=0.0,
            max=1.0,
            default=0.0, update=update_leaves
            )
    
        leafShape: EnumProperty(
    
            name='Leaf Shape',
    
            description='The shape of the leaves',
    
            items=(('hex', 'Hexagonal', '0'), ('rect', 'Rectangular', '1'),
                   ('dFace', 'DupliFaces', '2'), ('dVert', 'DupliVerts', '3')),
            default='hex', update=update_leaves
            )
    
        leafDupliObj: EnumProperty(
    
            name='Leaf Object',
    
            description='Object to use for leaf instancing if Leaf Shape is DupliFaces or DupliVerts',
            items=objectList,
    
            update=update_leaves
            )
        """
        bend = FloatProperty(
            name='Leaf Bend',
            description='The proportion of bending applied to the leaf (Bend)',
            min=0.0,
            max=1.0,
            default=0.0, update=update_leaves
            )
        """
    
        leafangle: FloatProperty(
    
            name='Leaf Angle',
    
            description='Leaf vertical attraction',
    
            default=0.0, update=update_leaves
            )
    
        horzLeaves: BoolProperty(
    
            name='Horizontal leaves',
    
            description='Leaves face upwards',
    
            default=True, update=update_leaves
            )
    
        leafDist: EnumProperty(
    
            name='Leaf Distribution',
    
    Andrew Hale's avatar
    Andrew Hale committed
            description='The way leaves are distributed on branches',
    
            default='6', update=update_tree
            )
    
        bevelRes: IntProperty(
    
            name='Bevel Resolution',
    
    Andrew Hale's avatar
    Andrew Hale committed
            description='The bevel resolution of the curves',
            min=0,
    
            default=0, update=update_tree
            )
    
        resU: IntProperty(
    
            name='Curve Resolution',
    
    Andrew Hale's avatar
    Andrew Hale committed
            description='The resolution along the curves',
            min=1,
    
            default=4, update=update_tree
            )
    
        handleType: EnumProperty(
    
            name='Handle Type',
    
    Andrew Hale's avatar
    Andrew Hale committed
            description='The type of handles used in the spline',
            items=handleList,
    
            default='0', update=update_tree
            )
    
        armAnim: BoolProperty(
    
            name='Armature Animation',
    
    Andrew Hale's avatar
    Andrew Hale committed
            description='Whether animation is added to the armature',
    
            default=False, update=update_tree
            )
    
        previewArm: BoolProperty(
    
            name='Fast Preview',
    
            description='Disable armature modifier, hide tree, and set bone display to wire, for fast playback',
    
            # Disable skin modifier and hide tree and armature, for fast playback
            default=False, update=update_tree
            )
    
        leafAnim: BoolProperty(
    
            name='Leaf Animation',
    
            description='Whether animation is added to the leaves',
    
            default=False, update=update_tree
            )
    
        frameRate: FloatProperty(
    
            name='Animation Speed',
    
            description=('Adjust speed of animation, relative to scene frame rate'),
            min=0.001,
    
            default=1, update=update_tree
            )
    
        loopFrames: IntProperty(
    
            name='Loop Frames',
    
            description='Number of frames to make the animation loop for, zero is disabled',
            min=0,
    
            default=0, update=update_tree
            )
        """
        windSpeed = FloatProperty(
            name='Wind Speed',
            description='The wind speed to apply to the armature',
            default=2.0, update=update_tree
            )
        windGust = FloatProperty(
            name='Wind Gust',
            description='The greatest increase over Wind Speed',
            default=0.0, update=update_tree
            )
        """
    
        wind: FloatProperty(
    
            name='Overall Wind Strength',
    
            description='The intensity of the wind to apply to the armature',
    
            default=1.0, update=update_tree
            )
    
        gust: FloatProperty(
    
            name='Wind Gust Strength',
    
            description='The amount of directional movement, (from the positive Y direction)',
    
            default=1.0, update=update_tree
            )
    
        gustF: FloatProperty(
    
            name='Wind Gust Fequency',
    
            description='The Frequency of directional movement',
    
            default=0.075, update=update_tree
            )
    
        af1: FloatProperty(
    
            name='Amplitude',
    
            description='Multiplier for noise amplitude',
    
            default=1.0, update=update_tree
            )
    
        af2: FloatProperty(
    
            name='Frequency',
    
            description='Multiplier for noise fequency',
    
            default=1.0, update=update_tree
            )
    
        af3: FloatProperty(
    
            name='Randomness',
    
            description='Random offset in noise',
    
            default=4.0, update=update_tree
            )
    
        makeMesh: BoolProperty(
    
            name='Make Mesh',
    
            description='Convert curves to mesh, uses skin modifier, enables armature simplification',
    
            default=False, update=update_tree
            )
    
        armLevels: IntProperty(
    
            name='Armature Levels',
    
            description='Number of branching levels to make bones for, 0 is all levels',
            min=0,
    
            default=2, update=update_tree
            )
    
        boneStep: IntVectorProperty(
    
            name='Bone Length',
    
            description='Number of stem segments per bone',
            min=1,
            default=[1, 1, 1, 1],
    
            size=4, update=update_tree
            )
    
        presetName: StringProperty(
    
            name='Preset Name',
    
    Andrew Hale's avatar
    Andrew Hale committed
            description='The name of the preset to be saved',
            default='',
    
            subtype='FILE_NAME', update=no_update_tree
            )
    
        limitImport: BoolProperty(
    
            name='Limit Import',
    
    Andrew Hale's avatar
    Andrew Hale committed
            description='Limited imported tree to 2 levels & no leaves for speed',
    
            default=True, update=no_update_tree
            )
    
        overwrite: BoolProperty(
    
            name='Overwrite',
    
            description='When checked, overwrite existing preset files when saving',
    
            default=False, update=no_update_tree
            )
        """
        startCurv = FloatProperty(
            name='Trunk Starting Angle',
            description=('The angle between vertical and the starting direction'
            'of the trunk'),
            min=0.0,
            max=360,
            default=0.0, update=update_tree
            )
        """
    
    Andrew Hale's avatar
    Andrew Hale committed
    
        @classmethod
        def poll(cls, context):
            return context.mode == 'OBJECT'
    
        def draw(self, context):
    
    Andrew Hale's avatar
    Andrew Hale committed
            layout = self.layout
    
    Andrew Hale's avatar
    Andrew Hale committed
            # Branch specs
    
            # layout.label(text='Tree Definition')
    
    Thomas Dinges's avatar
    Thomas Dinges committed
            layout.prop(self, 'chooseSet')
    
    Andrew Hale's avatar
    Andrew Hale committed
            if self.chooseSet == '0':
    
    Andrew Hale's avatar
    Andrew Hale committed
                box = layout.box()
    
                box.label(text="Geometry:")
    
    Thomas Dinges's avatar
    Thomas Dinges committed
                box.prop(self, 'bevel')
    
    Andrew Hale's avatar
    Andrew Hale committed
                row = box.row()
                row.prop(self, 'bevelRes')
                row.prop(self, 'resU')
    
    Thomas Dinges's avatar
    Thomas Dinges committed
                box.prop(self, 'handleType')
    
                box.prop(self, 'shape')
    
                col = box.column()
                col.prop(self, 'customShape')
    
                row = box.row()
                box.prop(self, 'shapeS')
                box.prop(self, 'branchDist')
                box.prop(self, 'nrings')
    
    Thomas Dinges's avatar
    Thomas Dinges committed
                box.prop(self, 'seed')
    
                box.label(text="Tree Scale:")
    
    Andrew Hale's avatar
    Andrew Hale committed
                row = box.row()
    
    Thomas Dinges's avatar
    Thomas Dinges committed
                row.prop(self, 'scale')
                row.prop(self, 'scaleV')
    
    Andrew Hale's avatar
    Andrew Hale committed
                # Here we create a dict of all the properties.
                # Unfortunately as_keyword doesn't work with vector properties,
                # so we need something custom. This is it
                data = []
    
                for a, b in (self.as_keywords(
                                        ignore=("chooseSet", "presetName", "limitImport",
                                                "do_update", "overwrite", "leafDupliObj"))).items():
    
    Andrew Hale's avatar
    Andrew Hale committed
                    # If the property is a vector property then add the slice to the list
                    try:
                        len(b)
                        data.append((a, b[:]))
    
    Andrew Hale's avatar
    Andrew Hale committed
                    # Otherwise, it is fine so just add it
    
    Andrew Hale's avatar
    Andrew Hale committed
                    except:
    
    Andrew Hale's avatar
    Andrew Hale committed
                        data.append((a, b))
                # Create the dict from the list
                data = dict(data)
    
                row = box.row()
                row.prop(self, 'presetName')
                # Send the data dict and the file name to the exporter
    
                row.operator('sapling.exportdata').data = repr([repr(data), self.presetName, self.overwrite])
                row = box.row()
    
                row.label(text=" ")
    
                row.prop(self, 'overwrite')
    
    Andrew Hale's avatar
    Andrew Hale committed
                row = box.row()
    
                row.menu('SAPLING_MT_preset', text='Load Preset')
    
    Andrew Hale's avatar
    Andrew Hale committed
                row.prop(self, 'limitImport')
    
    
    Thomas Dinges's avatar
    Thomas Dinges committed
            elif self.chooseSet == '1':
    
                box = layout.box()
    
                box.label(text="Branch Radius:")
    
    
                row = box.row()
                row.prop(self, 'bevel')
                row.prop(self, 'bevelRes')
    
                box.prop(self, 'ratio')
                row = box.row()
                row.prop(self, 'scale0')
                row.prop(self, 'scaleV0')
                box.prop(self, 'ratioPower')
    
                box.prop(self, 'minRadius')
                box.prop(self, 'closeTip')
                box.prop(self, 'rootFlare')
    
                box.prop(self, 'autoTaper')
    
                split = box.split()
                col = split.column()
                col.prop(self, 'taper')
                col = split.column()
                col.prop(self, 'radiusTweak')
    
            elif self.chooseSet == '2':
    
    Andrew Hale's avatar
    Andrew Hale committed
                box = layout.box()
    
                box.label(text="Branch Splitting:")
    
    Thomas Dinges's avatar
    Thomas Dinges committed
                box.prop(self, 'levels')
                box.prop(self, 'baseSplits')
    
                row = box.row()
                row.prop(self, 'baseSize')
                row.prop(self, 'baseSize_s')
                box.prop(self, 'splitHeight')
                box.prop(self, 'splitBias')
                box.prop(self, 'splitByLen')
    
    Thomas Dinges's avatar
    Thomas Dinges committed
                split = box.split()
    
    Thomas Dinges's avatar
    Thomas Dinges committed
                col = split.column()
    
    Andrew Hale's avatar
    Andrew Hale committed
                col.prop(self, 'branches')
                col.prop(self, 'splitAngle')
                col.prop(self, 'rotate')
    
                col.prop(self, 'attractOut')
    
    Thomas Dinges's avatar
    Thomas Dinges committed
                col = split.column()
                col.prop(self, 'segSplits')
                col.prop(self, 'splitAngleV')
    
    Andrew Hale's avatar
    Andrew Hale committed
                col.prop(self, 'rotateV')
    
    
                col.label(text="Branching Mode:")
    
                col.prop(self, 'rMode')
    
                box.column().prop(self, 'curveRes')
    
            elif self.chooseSet == '3':
    
    Andrew Hale's avatar
    Andrew Hale committed
                box = layout.box()
    
                box.label(text="Branch Growth:")
    
    
                box.prop(self, 'taperCrown')
    
    Thomas Dinges's avatar
    Thomas Dinges committed
                split = box.split()
    
    Thomas Dinges's avatar
    Thomas Dinges committed
                col = split.column()
    
    Andrew Hale's avatar
    Andrew Hale committed
                col.prop(self, 'length')
    
                col.prop(self, 'downAngle')
    
    Andrew Hale's avatar
    Andrew Hale committed
                col.prop(self, 'curve')
                col.prop(self, 'curveBack')
    
    Thomas Dinges's avatar
    Thomas Dinges committed
                col = split.column()
                col.prop(self, 'lengthV')