From 09e37c1f7048648952f3445b2e2e12445a653c8b Mon Sep 17 00:00:00 2001
From: Andrew Hale <TrumanBlending@gmail.com>
Date: Sat, 30 Jul 2011 09:59:19 +0000
Subject: [PATCH] Moved to Trunk

[[Split portion of a mixed commit.]]
---
 add_curve_ivygen.py | 687 ++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 687 insertions(+)
 create mode 100644 add_curve_ivygen.py

diff --git a/add_curve_ivygen.py b/add_curve_ivygen.py
new file mode 100644
index 000000000..d46503b1d
--- /dev/null
+++ b/add_curve_ivygen.py
@@ -0,0 +1,687 @@
+# ##### BEGIN GPL LICENSE BLOCK #####
+#
+#  This program is free software; you can redistribute it and/or
+#  modify it under the terms of the GNU General Public License
+#  as published by the Free Software Foundation; either version 2
+#  of the License, or (at your option) any later version.
+#
+#  This program is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#  GNU General Public License for more details.
+#
+#  You should have received a copy of the GNU General Public License
+#  along with this program; if not, write to the Free Software Foundation,
+#  Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+# ##### END GPL LICENSE BLOCK #####
+
+# <pep8-80 compliant>
+
+bl_info = {
+    "name": "IvyGen",
+    "author": "testscreenings, PKHG, TrumanBlending",
+    "version": (0, 1, 0),
+    "blender": (2, 5, 8),
+    "api": 38479,
+    "location": "View3D > Add > Curve",
+    "description": "Adds generated ivy to a mesh object starting at the 3D"\
+                   " cursor.",
+    "warning": "",
+    "wiki_url": "http://wiki.blender.org/index.php/Extensions:2.5/Py/"\
+                "Scripts/Curve/Ivy_Gen",
+    "tracker_url": "http://projects.blender.org/tracker/index.php?"\
+                   "func=detail&aid=27234",
+    "category": "Add Curve"}
+
+
+import bpy
+from bpy.props import FloatProperty, IntProperty, BoolProperty
+from mathutils import Vector, Matrix
+from collections import deque
+from math import pow, cos, pi, atan2
+from random import random as rand_val, seed as rand_seed
+import time
+
+
+def createIvyGeometry(IVY, growLeaves):
+    '''Create the curve geometry for IVY'''
+    # Compute the local size and the gauss weight filter
+    #local_ivyBranchSize = IVY.ivyBranchSize  # * radius * IVY.ivySize
+    gaussWeight = [1.0, 2.0, 4.0, 7.0, 9.0, 10.0, 9.0, 7.0, 4.0, 2.0, 1.0]
+
+    # Create a new curve and intialise it
+    curve = bpy.data.curves.new("IVY", type='CURVE')
+    curve.dimensions = '3D'
+    curve.bevel_depth = 1
+    curve.use_fill_front = curve.use_fill_back = False
+
+    if growLeaves:
+        # Create the ivy leaves
+        # Order location of the vertices
+        signList = [(-1, 1), (1, 1), (1, -1), (-1, -1)]
+
+        # Get the local size
+        #local_ivyLeafSize = IVY.ivyLeafSize  # * radius * IVY.ivySize
+
+        # Initialise the vertex and face lists
+        vertList = deque()
+
+        # Store the methods for faster calling
+        addV = vertList.extend
+        rotMat = Matrix.Rotation
+
+    # Loop over all roots to generate its nodes
+    for root in IVY.ivyRoots:
+        # Only grow if more than one node
+        numNodes = len(root.ivyNodes)
+        if numNodes > 1:
+            # Calculate the local radius
+            local_ivyBranchRadius = 1 / (root.parents + 1) + 1
+            prevIvyLength = 1 / root.ivyNodes[-1].length
+            splineVerts = [ax for n in root.ivyNodes for ax in n.pos.to_4d()]
+
+            radiusConstant = local_ivyBranchRadius * IVY.ivyBranchSize
+            splineRadii = [radiusConstant * (1.3 - n.length * prevIvyLength)
+                                                        for n in root.ivyNodes]
+
+            # Add the poly curve and set coords and radii
+            newSpline = curve.splines.new(type='POLY')
+            newSpline.points.add(len(splineVerts) // 4 - 1)
+            newSpline.points.foreach_set('co', splineVerts)
+            newSpline.points.foreach_set('radius', splineRadii)
+
+            # Loop over all nodes in the root
+            for i, n in enumerate(root.ivyNodes):
+                for k in range(len(gaussWeight)):
+                    idx = max(0, min(i + k - 5, numNodes - 1))
+                    n.smoothAdhesionVector += (gaussWeight[k] *
+                                             root.ivyNodes[idx].adhesionVector)
+                n.smoothAdhesionVector /= 56.0
+                n.adhesionLength = n.smoothAdhesionVector.length
+                n.smoothAdhesionVector.normalize()
+
+                if growLeaves and (i < numNodes - 1):
+                    node = root.ivyNodes[i]
+                    nodeNext = root.ivyNodes[i + 1]
+
+                    # Find the weight and normalise the smooth adhesion vector
+                    weight = pow(node.length * prevIvyLength, 0.7)
+
+                    # Calculate the ground ivy and the new weight
+                    groundIvy = max(0.0, -node.smoothAdhesionVector.z)
+                    weight += groundIvy * pow(1 - node.length *
+                                                              prevIvyLength, 2)
+
+                    # Find the alignment weight
+                    alignmentWeight = node.adhesionLength
+
+                    # Calculate the needed angles
+                    phi = atan2(node.smoothAdhesionVector.y,
+                                          node.smoothAdhesionVector.x) - pi / 2
+
+                    theta = (0.5 *
+                        node.smoothAdhesionVector.angle(Vector((0, 0, -1)), 0))
+
+                    # Find the size weight
+                    sizeWeight = 1.5 - (cos(2 * pi * weight) * 0.5 + 0.5)
+
+                    # Randomise the angles
+                    phi += (rand_val() - 0.5) * (1.3 - alignmentWeight)
+                    theta += (rand_val() - 0.5) * (1.1 - alignmentWeight)
+
+                    # Calculate the leaf size an append the face to the list
+                    leafSize = IVY.ivyLeafSize * sizeWeight
+
+                    for j in range(10):
+                        # Generate the probability
+                        probability = rand_val()
+
+                        # If we need to grow a leaf, do so
+                        if (probability * weight) > IVY.leafProbability:
+
+                            # Generate the random vector
+                            randomVector = Vector((rand_val() - 0.5,
+                                           rand_val() - 0.5, rand_val() - 0.5))
+
+                            # Find the leaf center
+                            center = node.pos.lerp(nodeNext.pos, j / 10) +\
+                                               IVY.ivyLeafSize * randomVector
+
+                            # For each of the verts, rotate/scale and append
+                            basisVecX = Vector((1, 0, 0))
+                            basisVecY = Vector((0, 1, 0))
+
+                            horiRot = rotMat(theta, 3, 'X')
+                            vertRot = rotMat(phi, 3, 'Z')
+
+                            basisVecX.rotate(horiRot)
+                            basisVecY.rotate(horiRot)
+
+                            basisVecX.rotate(vertRot)
+                            basisVecY.rotate(vertRot)
+
+                            basisVecX *= leafSize
+                            basisVecY *= leafSize
+
+                            addV([k1 * basisVecX + k2 * basisVecY + center for
+                                                           k1, k2 in signList])
+
+    # Add the object and link to scene
+    newCurve = bpy.data.objects.new("IVY_Curve", curve)
+    bpy.context.scene.objects.link(newCurve)
+
+    if growLeaves:
+        faceList = [[4 * i + l for l in range(4)] for i in
+                                                     range(len(vertList) // 4)]
+
+        # Generate the new leaf mesh and link
+        me = bpy.data.meshes.new('IvyLeaf')
+        me.from_pydata(vertList, [], faceList)
+        me.update(calc_edges=True)
+        ob = bpy.data.objects.new('IvyLeaf', me)
+        bpy.context.scene.objects.link(ob)
+
+        tex = me.uv_textures.new("Leaves")
+
+        # Set the uv texture coords
+        for d in tex.data:
+            uv1, uv2, uv3, uv4 = signList
+
+        ob.parent = newCurve
+
+
+def computeBoundingSphere(ob):
+    # Get the mesh data
+    me = ob.data
+    # Intialise the center
+    center = Vector((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',
+                 'smoothAdhesionVector', 'length', 'floatingLength', 'climb')
+
+    def __init__(self):
+        self.pos = Vector((0, 0, 0))
+        self.primaryDir = Vector((0, 0, 1))
+        self.adhesionVector = Vector((0, 0, 0))
+        self.smoothAdhesionVector = Vector((0, 0, 0))
+        self.length = 0.0001
+        self.floatingLength = 0.0
+        self.climb = True
+
+
+class IvyRoot:
+    """ The class used to hold all ivy nodes growing from this root point."""
+    __slots__ = ('ivyNodes', 'alive', 'parents')
+
+    def __init__(self):
+        self.ivyNodes = deque()
+        self.alive = True
+        self.parents = 0
+
+
+class Ivy:
+    """ The class holding all parameters and ivy roots."""
+    __slots__ = ('ivyRoots', 'primaryWeight', 'randomWeight',
+                 'gravityWeight', 'adhesionWeight', 'branchingProbability',
+                 'leafProbability', 'ivySize', 'ivyLeafSize', 'ivyBranchSize',
+                 'maxFloatLength', 'maxAdhesionDistance', 'maxLength')
+
+    def __init__(self,
+                 primaryWeight=0.5,
+                 randomWeight=0.2,
+                 gravityWeight=1.0,
+                 adhesionWeight=0.1,
+                 branchingProbability=0.05,
+                 leafProbability=0.35,
+                 ivySize=0.02,
+                 ivyLeafSize=0.02,
+                 ivyBranchSize=0.001,
+                 maxFloatLength=0.5,
+                 maxAdhesionDistance=1.0):
+
+        self.ivyRoots = deque()
+        self.primaryWeight = primaryWeight
+        self.randomWeight = randomWeight
+        self.gravityWeight = gravityWeight
+        self.adhesionWeight = adhesionWeight
+        self.branchingProbability = 1 - branchingProbability
+        self.leafProbability = 1 - leafProbability
+        self.ivySize = ivySize
+        self.ivyLeafSize = ivyLeafSize
+        self.ivyBranchSize = ivyBranchSize
+        self.maxFloatLength = maxFloatLength
+        self.maxAdhesionDistance = maxAdhesionDistance
+        self.maxLength = 0.0
+
+        # Normalise all the weights only on intialisation
+        sum = self.primaryWeight + self.randomWeight + self.adhesionWeight
+        self.primaryWeight /= sum
+        self.randomWeight /= sum
+        self.adhesionWeight /= sum
+
+    def seed(self, seedPos):
+        # Seed the Ivy by making a new root and first node
+        tmpRoot = IvyRoot()
+        tmpIvy = IvyNode()
+        tmpIvy.pos = seedPos
+
+        tmpRoot.ivyNodes.append(tmpIvy)
+        self.ivyRoots.append(tmpRoot)
+
+    def grow(self, ob):
+        # Determine the local sizes
+        #local_ivySize = self.ivySize  # * radius
+        #local_maxFloatLength = self.maxFloatLength  # * radius
+        #local_maxAdhesionDistance = self.maxAdhesionDistance  # * radius
+
+        for root in self.ivyRoots:
+            # Make sure the root is alive, if not, skip
+            if not root.alive:
+                continue
+
+            # Get the last node in the current root
+            prevIvy = root.ivyNodes[-1]
+
+            # If the node is floating for too long, kill the root
+            if prevIvy.floatingLength > self.maxFloatLength:
+                root.alive = False
+
+            # Set the primary direction from the last node
+            primaryVector = prevIvy.primaryDir
+
+            # Make the random vector and normalise
+            randomVector = Vector((rand_val() - 0.5, rand_val() - 0.5,
+                                   rand_val() - 0.5)) + Vector((0, 0, 0.2))
+            randomVector.normalize()
+
+            # Calculate the adhesion vector
+            adhesionVector = adhesion(prevIvy.pos, ob,
+                                                      self.maxAdhesionDistance)
+
+            # Calculate the growing vector
+            growVector = self.ivySize * (primaryVector * self.primaryWeight +
+                                          randomVector * self.randomWeight +
+                                          adhesionVector * self.adhesionWeight)
+
+            # Find the gravity vector
+            gravityVector = (self.ivySize * self.gravityWeight *
+                                                            Vector((0, 0, -1)))
+            gravityVector *= pow(prevIvy.floatingLength / self.maxFloatLength,
+                                 0.7)
+
+            # Determine the new position vector
+            newPos = prevIvy.pos + growVector + gravityVector
+
+            # Check for collisions with the object
+            climbing = collision(ob, prevIvy.pos, newPos)
+
+            # Update the growing vector for any collisions
+            growVector = newPos - prevIvy.pos - gravityVector
+            growVector.normalize()
+
+            # Create a new IvyNode and set its properties
+            tmpNode = IvyNode()
+            tmpNode.climb = climbing
+            tmpNode.pos = newPos
+            tmpNode.primaryDir = prevIvy.primaryDir.lerp(growVector, 0.5)
+            tmpNode.primaryDir.normalize()
+            tmpNode.adhesionVector = adhesionVector
+            tmpNode.length = prevIvy.length + (newPos - prevIvy.pos).length
+
+            if tmpNode.length > self.maxLength:
+                self.maxLength = tmpNode.length
+
+            # If the node isn't climbing, update it's floating length
+            # Otherwise set it to 0
+            if not climbing:
+                tmpNode.floatingLength = prevIvy.floatingLength + (newPos -
+                                                            prevIvy.pos).length
+            else:
+                tmpNode.floatingLength = 0.0
+
+            root.ivyNodes.append(tmpNode)
+
+        # Loop through all roots to check if a new root is generated
+        for root in self.ivyRoots:
+            # Check the root is alive and isn't at high level of recursion
+            if (root.parents > 3) or (not root.alive):
+                continue
+
+            # Check to make sure there's more than 1 node
+            if len(root.ivyNodes) > 1:
+                # Loop through all nodes in root to check if new root is grown
+                for node in root.ivyNodes:
+                    # Set the last node of the root and find the weighting
+                    prevIvy = root.ivyNodes[-1]
+                    weight = 1.0 - (cos(2.0 * pi * node.length /
+                                        prevIvy.length) * 0.5 + 0.5)
+
+                    probability = rand_val()
+
+                    # Check if a new root is grown and if so, set its values
+                    if (probability * weight > self.branchingProbability):
+                        tmpNode = IvyNode()
+                        tmpNode.pos = node.pos
+                        tmpNode.floatingLength = node.floatingLength
+
+                        tmpRoot = IvyRoot()
+                        tmpRoot.parents = root.parents + 1
+
+                        tmpRoot.ivyNodes.append(tmpNode)
+                        self.ivyRoots.append(tmpRoot)
+                        return
+
+
+def adhesion(loc, ob, max_l):
+    # Get transfor vector and transformed loc
+    tran_mat = ob.matrix_world.inverted()
+    tran_loc = tran_mat * loc
+
+    # Compute the adhesion vector by finding the nearest point
+    nearest_result = ob.closest_point_on_mesh(tran_loc, max_l)
+    adhesion_vector = Vector((0, 0, 0))
+    if nearest_result[2] != -1:
+        # Compute the distance to the nearest point
+        adhesion_vector = ob.matrix_world * nearest_result[0] - loc
+        distance = adhesion_vector.length
+        # If it's less than the maximum allowed and not 0, continue
+        if distance:
+            # Compute the direction vector between the closest point and loc
+            adhesion_vector.normalize()
+            adhesion_vector *= 1.0 - distance / max_l
+            #adhesion_vector *= getFaceWeight(ob.data, nearest_result[2])
+    return adhesion_vector
+
+
+def collision(ob, pos, new_pos):
+    # Check for collision with the object
+    climbing = False
+
+    # Transform vecs
+    tran_mat = ob.matrix_world.inverted()
+    tran_pos = tran_mat * pos
+    tran_new_pos = tran_mat * new_pos
+
+    ray_result = ob.ray_cast(tran_pos, tran_new_pos)
+    # If there's a collision we need to check it
+    if ray_result[2] != -1:
+        # Check whether the collision is going into the object
+        if (tran_new_pos - tran_pos).dot(ray_result[1]) < 0.0:
+            # Find projection of the piont onto the plane
+            p0 = tran_new_pos - (tran_new_pos -
+                                          ray_result[0]).project(ray_result[1])
+            # Reflect in the plane
+            tran_new_pos += 2 * (p0 - tran_new_pos)
+            new_pos *= 0
+            new_pos += ob.matrix_world * tran_new_pos
+            climbing = True
+    return climbing
+
+
+class IvyGen(bpy.types.Operator):
+    bl_idname = "curve.ivy_gen"
+    bl_label = "IvyGen"
+    bl_options = {'REGISTER', 'UNDO'}
+
+    maxIvyLength = FloatProperty(name="Max Ivy Length",
+                    description="Maximum ivy length in Blender Units.",
+                    default=1.0,
+                    min=0.0,
+                    soft_max=3.0,
+                    subtype='DISTANCE',
+                    unit='LENGTH')
+    primaryWeight = FloatProperty(name="Primary Weight",
+                    description="Weighting given to the current direction.",
+                    default=0.5,
+                    min=0.0,
+                    soft_max=1.0)
+    randomWeight = FloatProperty(name="Random Weight",
+                    description="Weighting given to the random direction.",
+                    default=0.2,
+                    min=0.0,
+                    soft_max=1.0)
+    gravityWeight = FloatProperty(name="Gravity Weight",
+                    description="Weighting given to the gravity direction.",
+                    default=1.0,
+                    min=0.0,
+                    soft_max=1.0)
+    adhesionWeight = FloatProperty(name="Adhesion Weight",
+                    description="Weighting given to the adhesion direction.",
+                    default=0.1,
+                    min=0.0,
+                    soft_max=1.0)
+    branchingProbability = FloatProperty(name="Branching Probability",
+                    description="Probability of a new branch forming.",
+                    default=0.05,
+                    min=0.0,
+                    soft_max=1.0)
+    leafProbability = FloatProperty(name="Leaf Probability",
+                    description="Probability of a leaf forming.",
+                    default=0.35,
+                    min=0.0,
+                    soft_max=1.0)
+    ivySize = FloatProperty(name="Ivy Size",
+                    description="The length of an ivy segment in Blender"\
+                                " Units.",
+                    default=0.02,
+                    min=0.0,
+                    soft_max=1.0,
+                    precision=3)
+    ivyLeafSize = FloatProperty(name="Ivy Leaf Size",
+                    description="The size of the ivy leaves",
+                    default=0.02,
+                    min=0.0,
+                    soft_max=0.5,
+                    precision=3)
+    ivyBranchSize = FloatProperty(name="Ivy Branch Size",
+                    description="The size of the ivy branches",
+                    default=0.001,
+                    min=0.0,
+                    soft_max=0.1,
+                    precision=4)
+    maxFloatLength = FloatProperty(name="Max Float Length",
+                    description="The maximum distance that a branch"\
+                                "can live while floating.",
+                    default=0.5,
+                    min=0.0,
+                    soft_max=1.0)
+    maxAdhesionDistance = FloatProperty(name="Max Adhesion Length",
+                    description="The maximum distance that a branch"\
+                                "will feel the effects of adhesion.",
+                    default=1.0,
+                    min=0.0,
+                    soft_max=2.0,
+                    precision=2)
+    randomSeed = FloatProperty(name="Random Seed",
+                    description="The seed governing random generation.",
+                    default=0,
+                    min=0.0,
+                    soft_max=10)
+    maxTime = FloatProperty(name="Maximum Time",
+                    description="The maximum time to run the generation for"\
+                                "in seconds generation (0.0 = Disabled)",
+                    default=0.0,
+                    min=0.0,
+                    soft_max=10)
+    growLeaves = BoolProperty(name="Grow Leaves",
+                    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
+        if (ob is not None) and\
+           (ob.type == 'MESH') and\
+           (context.mode == 'OBJECT'):
+            return True
+        return False
+
+    def execute(self, context):
+        if self.updateIvy:
+            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
+
+            # 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(int(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)
+
+            checkAlive = True
+            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'}
+
+        return {'PASS_THROUGH'}
+
+    def draw(self, context):
+        layout = self.layout
+        row = layout.row()
+        row.alignment = 'EXPAND'
+        row.prop(self, 'updateIvy', icon='CURVE_DATA')
+
+        row = layout.row()
+        properties = row.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
+
+        row = layout.row()
+        row.operator('curve.ivy_gen', text="Add New Default Ivy")
+
+        row = layout.row()
+        row.prop(self, 'growLeaves')
+
+        box = layout.box()
+        box.label("Generation Settings")
+        row = box.row()
+        row.prop(self, 'randomSeed')
+        row = box.row()
+        row.prop(self, 'maxTime')
+        box = layout.box()
+        box.label("Size Settings")
+        row = box.row()
+        row.prop(self, 'maxIvyLength')
+        row = box.row()
+        row.prop(self, 'ivySize')
+        row = box.row()
+        row.prop(self, 'maxFloatLength')
+        row = box.row()
+        row.prop(self, 'maxAdhesionDistance')
+        box = layout.box()
+        box.label("Weight Settings")
+        row = box.row()
+        row.prop(self, 'primaryWeight')
+        row = box.row()
+        row.prop(self, 'randomWeight')
+        row = box.row()
+        row.prop(self, 'gravityWeight')
+        row = box.row()
+        row.prop(self, 'adhesionWeight')
+        box = layout.box()
+        box.label("Branch Settings")
+        row = box.row()
+        row.prop(self, 'branchingProbability')
+        row = box.row()
+        row.prop(self, 'ivyBranchSize')
+
+        if self.growLeaves:
+            box = layout.box()
+            box.label("Leaf Settings")
+            row = box.row()
+            row.prop(self, 'ivyLeafSize')
+            row = box.row()
+            row.prop(self, 'leafProbability')
+
+
+def menu_func(self, context):
+    self.layout.operator(IvyGen.bl_idname, text="Add Ivy to Mesh",
+                                                icon='PLUGIN').updateIvy = True
+
+
+def register():
+    bpy.utils.register_module(__name__)
+    bpy.types.INFO_MT_curve_add.append(menu_func)
+
+
+def unregister():
+    bpy.types.INFO_MT_curve_add.remove(menu_func)
+    bpy.utils.unregister_module(__name__)
+
+
+if __name__ == "__main__":
+    register()
-- 
GitLab