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

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCampbell Barton <ideasman42@gmail.com>2007-11-08 00:39:23 +0300
committerCampbell Barton <ideasman42@gmail.com>2007-11-08 00:39:23 +0300
commit32758217590e98e810f6d522d4db24c50284bf08 (patch)
tree56c976e7e1c1f75d1be84f0ad6411dc68cf9d73e /release
parentb4ec6efb41bbf05c6811da9276e7697939ed3ee1 (diff)
(work in progress commit) - tree wizard, makes a subsurfed skin from curves. also adds UV's (and soon bones)
needs a user interface.
Diffstat (limited to 'release')
-rw-r--r--release/scripts/wizard_curve2tree.py1121
1 files changed, 1121 insertions, 0 deletions
diff --git a/release/scripts/wizard_curve2tree.py b/release/scripts/wizard_curve2tree.py
new file mode 100644
index 00000000000..56339a2a8e9
--- /dev/null
+++ b/release/scripts/wizard_curve2tree.py
@@ -0,0 +1,1121 @@
+import bpy
+import Blender
+from Blender.Mathutils import Vector, CrossVecs, AngleBetweenVecs, LineIntersect, TranslationMatrix, ScaleMatrix
+from Blender.Geometry import ClosestPointOnLine
+
+def debug_pt(co):
+ Blender.Window.SetCursorPos(tuple(co))
+ Blender.Window.RedrawAll()
+ print 'debugging', co
+
+
+def closestVecIndex(vec, vecls):
+ best= -1
+ best_dist = 100000000
+ for i, vec_test in enumerate(vecls):
+ dist = (vec-vec_test).length
+ if dist < best_dist:
+ best = i
+ best_dist = dist
+
+ return best
+
+eul = 0.00001
+
+class tree:
+ def __init__(self):
+ self.branches_all = []
+ self.branches_root = []
+ self.mesh = None
+ self.object = None
+ self.limbScale = 1.0
+
+ self.debug_objects = []
+
+ def fromCurve(self, object):
+
+ curve = object.data
+
+ # Set the curve object scale
+ if curve.bevob:
+ # A bit of a hack to guess the size of the curve object if you have one.
+ bb = curve.bevob.boundingBox
+ # self.limbScale = (bb[0] - bb[7]).length / 2.825 # THIS IS GOOD WHEN NON SUBSURRFED
+ self.limbScale = (bb[0] - bb[7]).length / 1.8
+ # end
+
+
+ # Get the curve points as bpoints
+ for spline in curve:
+ brch = branch()
+ self.branches_all.append(brch)
+
+ for bez in spline:
+ # calc normal vector later
+ pt = bpoint(brch, Vector(bez.vec[1]), Vector(), bez.radius * self.limbScale)
+ brch.bpoints.append( pt )
+
+
+ # Get the curve as a mesh. - for inbetween points
+ tmpme = bpy.data.meshes.new()
+
+ # remove/backup bevel ob
+ bev_back = curve.bevob
+ if bev_back: curve.bevob = None
+
+ # get the curve mesh data
+ tmpob = bpy.data.scenes.active.objects.new( curve )
+ tmpme.getFromObject(object)
+ bpy.data.scenes.active.objects.unlink(tmpob)
+
+ # restore bevel ob
+ if bev_back:
+ curve.bevob = bev_back
+
+ # Guess the size of the curve object if you have one. This is not perfect but good enough
+ bb = bev_back.boundingBox
+ self.limbScale = (bb[0] - bb[7]).length / 2.825
+
+
+
+ # TEMP FOR TESTING
+ # bpy.data.scenes.active.objects.new(tmpme)
+
+ vecs = [ v.co for v in tmpme.verts ]
+ del tmpme
+
+ # for branch
+ #used_points = set()
+ for brch in self.branches_all:
+ offset = 0
+ for i in xrange(1, len(brch.bpoints)):
+ # find the start/end points
+ start_pt = brch.bpoints[offset+i-1]
+ end_pt = brch.bpoints[offset+i]
+
+ start = end = None
+ for j, co in enumerate(vecs):
+ if start == None:
+ if (co-start_pt.co).length < eul:
+ start = j
+ if end == None:
+ if (co-end_pt.co).length < eul:
+ end = j
+ if start != None and end != None:
+ break
+
+ # for now we assuem the start is always a lower index.
+ #if start > end:
+ # raise "error index is not one we like"
+
+ #used_points.add(start)
+ #used_points.add(end)
+ radius = start_pt.radius
+
+ #print 'coords', start_co, end_co
+ #### print "starting", start, end
+ if start > end:
+ j = start-1
+ raise "some bug!"
+ else:
+ j = start+1
+
+ step = 1
+ step_tot = abs(start-end)
+ while j!=end:
+ #radius = (start_pt.radius*(step_tot-step) - end_pt.radius*step ) / step_tot
+ w1 = step_tot-step
+ w2 = step
+
+ radius = ((start_pt.radius*w1) + (end_pt.radius*w2)) / step_tot
+
+ #### print i,j, radius
+ pt = bpoint(brch, Vector(vecs[j]), Vector(), radius)
+ brch.bpoints.insert(offset+i, pt)
+ offset+=1
+
+ if start > end:
+ j-=1
+ else:
+ j+=1
+
+ step +=1
+
+ # Now calculate the normals
+ for brch in self.branches_all:
+ for i in xrange(1, len(brch.bpoints)-1):
+ brch.bpoints[i].next = brch.bpoints[i+1]
+ brch.bpoints[i].prev = brch.bpoints[i-1]
+
+ brch.bpoints[0].next = brch.bpoints[1]
+ brch.bpoints[-1].prev = brch.bpoints[-2]
+
+
+ for pt in brch.bpoints:
+ pt.calcNormal()
+ pt.calcNextMidCo()
+
+ # remove segments
+ # We may want to remove segments for 2 reasons
+ # 1) - too high resolution
+ # 2) - too close together (makes yucky geometry)
+
+ def resetTags(self, value):
+ for brch in self.branches_all:
+ brch.tag = value
+
+ def buildConnections(self, sloppy=1.0, stem_trim = 1.0):
+ '''
+ build tree data - fromCurve must run first
+
+ '''
+
+ # Connect branches
+ for i in xrange(len(self.branches_all)):
+
+ brch_i = self.branches_all[i]
+ brch_i_head_pt = brch_i.bpoints[0]
+
+ for j in xrange(len(self.branches_all)):
+ if i != j:
+ # See if any of the points match this branch
+ # see if Branch 'i' is the child of branch 'j'
+
+ brch_j = self.branches_all[j]
+
+ best_j, dist = brch_j.findClosest(brch_i_head_pt.co)
+
+ # Check its in range, allow for a bit out - hense the 1.5
+ if dist < best_j.radius * sloppy:
+
+ # Remove points that are within the radius, so as to not get ugly joins
+ # TODO - dont remove the whole branch
+ while len(brch_i.bpoints)>2 and (brch_i.bpoints[0].co - best_j.nextMidCo).length < best_j.radius * stem_trim:
+ del brch_i.bpoints[0]
+ brch_i.bpoints[0].prev = None
+
+ brch_i.parent_pt = best_j
+
+ # addas a member of best_j.children later when we have the geometry info available.
+
+ #### print "Found Connection!!!", i, j
+ break # go onto the next branch
+
+ """
+ children = [brch_child for brch_child in pt.children]
+ if children:
+ # This pt is one side of the segment, pt.next joins this segment.
+ # calculate the median point the 2 segments would span
+ # Once this is done we need to adjust 2 things
+ # 1) move both segments up/down so they match the branches best.
+ # 2) set the spacing of the segments around the point.
+
+
+ # First try to get the ideal some space around each joint
+ # the spacing shoule be an average of
+ for brch.bpoints:
+ """
+
+
+ # Calc points with dependancies
+ # detect circular loops!!! - TODO
+ done_nothing = False
+ self.resetTags(False)
+ while done_nothing == False:
+ done_nothing = True
+ for brch in self.branches_all:
+
+ if brch.tag == False and (brch.parent_pt == None or brch.parent_pt.branch.tag == True):
+
+ # Assign this to a spesific side of the parents point
+ # we know this is a child but not which side it should be attached to.
+ if brch.parent_pt:
+ child_locs = [\
+ brch.parent_pt.childPoint(0),\
+ brch.parent_pt.childPoint(1),\
+ brch.parent_pt.childPoint(2),\
+ brch.parent_pt.childPoint(3)]
+
+ best_idx = closestVecIndex(brch.bpoints[0].co, child_locs)
+ brch.parent_pt.children[best_idx] = brch
+ # DONE
+
+ done_nothing = False
+
+ for pt in brch.bpoints:
+ # for temp debugging
+ ## self.mesh.faces.extend([pt.verts])
+ pt.calcVerts()
+ # pt.toMesh(self.mesh) # Cant do here because our children arnt calculated yet!
+
+ brch.tag = True
+
+ def optimizeSpacing(self, density=1.0, joint_compression=1.0):
+ '''
+ Optimize spacing, taking branch hierarchy children into account,
+ can add or subdivide segments so branch joins dont look horrible.
+ '''
+ for brch in self.branches_all:
+ brch.evenJointDistrobution(joint_compression)
+
+ # Correct points that were messed up from sliding
+ # This happens when one point is pushed past another and the branch gets an overlaping line
+ for brch in self.branches_all:
+ brch.fixOverlapError()
+
+ # Collapsing
+ for brch in self.branches_all:
+ brch.collapsePoints(density)
+
+ for brch in self.branches_all:
+ brch.branchReJoin()
+
+
+ def toDebugDisplay(self):
+ '''
+ Should be able to call this at any time to see whats going on
+ '''
+ sce = bpy.data.scenes.active
+
+ for ob in self.debug_objects:
+ for ob in sce.objects:
+ sce.objects.unlink(ob)
+
+ for branch_index, brch in enumerate(self.branches_all):
+ pt_index = 0
+ for pt_index, pt in enumerate(brch.bpoints):
+ name = '%.3d_%.3d' % (branch_index, pt_index)
+ if pt.next==None:
+ name += '_end'
+ if pt.prev==None:
+ name += '_start'
+
+ ob = sce.objects.new('Empty', name)
+ self.debug_objects.append(ob)
+ mat = ScaleMatrix(pt.radius, 4) * TranslationMatrix(pt.co)
+ ob.setMatrix(mat)
+ ob.setDrawMode(8) # drawname
+ Blender.Window.RedrawAll()
+
+
+
+ def toMesh(self, mesh=None, do_uvmap=True, do_uv_scalewidth=True, uv_image = None):
+ # Simple points
+ '''
+ self.mesh = bpy.data.meshes.new()
+ self.object = bpy.data.scenes.active.objects.new(self.mesh)
+ self.mesh.verts.extend([ pt.co for brch in self.branches_all for pt in brch.bpoints ])
+ '''
+ if mesh:
+ self.mesh = mesh
+ mesh.verts = None
+ else:
+ self.mesh = bpy.data.meshes.new()
+
+ totverts = 0
+
+ for brch in self.branches_all:
+ totverts += len(brch.bpoints)
+
+ self.mesh.verts.extend( [ (0.0,0.0,0.0) ] * ((totverts * 4)+1) ) # +1 is a dummy vert
+ verts = self.mesh.verts
+
+ # Assign verts to points, 4 verts for each point.
+ i = 1 # dummy vert, should be 0
+ for brch in self.branches_all:
+ for pt in brch.bpoints:
+ pt.verts[0] = verts[i]
+ pt.verts[1] = verts[i+1]
+ pt.verts[2] = verts[i+2]
+ pt.verts[3] = verts[i+3]
+ i+=4
+
+ # Do this again because of collapsing
+ # pt.calcVerts(brch)
+
+ # roll the tube so quads best meet up to their branches.
+ for brch in self.branches_all:
+ #for pt in brch.bpoints:
+ if brch.parent_pt:
+
+ # Use temp lists for gathering an average
+ if brch.parent_pt.roll_angle == None:
+ brch.parent_pt.roll_angle = [brch.getParentQuadAngle()]
+ # More then 2 branches use this point, add to the list
+ else:
+ brch.parent_pt.roll_angle.append( brch.getParentQuadAngle() )
+
+ # average the temp lists into floats
+ for brch in self.branches_all:
+ #for pt in brch.bpoints:
+ if brch.parent_pt and type(brch.parent_pt.roll_angle) == list:
+ # print brch.parent_pt.roll_angle
+ f = 0.0
+ for val in brch.parent_pt.roll_angle:
+ f += val
+ brch.parent_pt.roll_angle = f/len(brch.parent_pt.roll_angle)
+
+ # set the roll of all the first segments that have parents,
+ # this is because their roll is set from their parent quad and we dont want them to roll away from that.
+ for brch in self.branches_all:
+ if brch.parent_pt:
+ # if the first joint has a child then apply half the roll
+ # theres no correct solition here, but this seems ok
+ if brch.bpoints[0].roll_angle != None:
+ #brch.bpoints[0].roll_angle *= 0.5
+ #brch.bpoints[0].roll_angle = 0.0
+ #brch.bpoints[1].roll_angle = 0.0
+ brch.bpoints[0].roll_angle = 0
+ pass
+ else:
+ # our roll was set from the branches parent and needs no changing
+ # set it to zero so the following functions know to interpolate.
+ brch.bpoints[0].roll_angle = 25.0
+ #brch.bpoints[1].roll_angle = 0.0
+
+ '''
+ Now interpolate the roll!
+ The method used here is a little odd.
+
+ * first loop up the branch and set each points value to the "last defined" value and record the steps
+ since the last defined value
+ * Do the same again but backwards
+
+ now for each undefined value we have 1 or 2 values, if its 1 its simple we just use that value
+ ( no interpolation ), if there are 2 then we use the offsets from each end to work out the interpolation.
+
+ one up, one back, and another to set the values, so 3 loops all up.
+ '''
+ #### print "scan up the branch..."
+ for brch in self.branches_all:
+ last_value = None
+ last_index = -1
+ for i in xrange(len(brch.bpoints)):
+ pt = brch.bpoints[i]
+ if type(pt.roll_angle) in (float, int):
+ last_value = pt.roll_angle
+ last_index = i
+ else:
+ if type(last_value) in (float, int):
+ # Assign a list, because there may be a connecting roll value from another joint
+ pt.roll_angle = [(last_value, i-last_index)]
+
+ #### print "scan down the branch..."
+ last_value = None
+ last_index = -1
+ for i in xrange(len(brch.bpoints)-1, -1, -1): # same as above but reverse
+ pt = brch.bpoints[i]
+ if type(pt.roll_angle) in (float, int):
+ last_value = pt.roll_angle
+ last_index = i
+ else:
+ if last_value != None:
+ if type(pt.roll_angle) == list:
+ pt.roll_angle.append((last_value, last_index-i))
+ else:
+ #pt.roll_angle = [(last_value, last_index-i)]
+
+ # Dont bother assigning a list because we wont need to add to it later
+ pt.roll_angle = last_value
+
+ # print "looping ,...."
+ ### print "assigning/interpolating roll values"
+ for pt in brch.bpoints:
+
+ # print "this roll IS", pt.roll_angle
+
+ if pt.roll_angle == None:
+ continue
+ elif type(pt.roll_angle) in (float, int):
+ pass
+ elif len(pt.roll_angle) == 1:
+ pt.roll_angle = pt.roll_angle[0][0]
+ else:
+ # interpolate
+ tot = pt.roll_angle[0][1] + pt.roll_angle[1][1]
+ pt.roll_angle = \
+ (pt.roll_angle[0][0] * (tot - pt.roll_angle[0][1]) +\
+ pt.roll_angle[1][0] * (tot - pt.roll_angle[1][1])) / tot
+
+ #### print pt.roll_angle, 'interpolated roll'
+
+ pt.roll(pt.roll_angle)
+
+ # Done with temp average list. now we know the best roll for each branch.
+
+ # mesh the data
+ for brch in self.branches_all:
+ for pt in brch.bpoints:
+ pt.toMesh(self.mesh)
+
+ faces = self.mesh.faces
+ faces.extend([ face for brch in self.branches_all for pt in brch.bpoints for face in pt.faces if face ])
+
+ if do_uvmap:
+ # Assign the faces back
+ face_index = 0
+ for brch in self.branches_all:
+ for pt in brch.bpoints:
+ for i in (0,1,2,3):
+ if pt.faces[i]:
+ pt.faces[i] = faces[face_index]
+ face_index +=1
+
+ self.mesh.faceUV = True
+ for brch in self.branches_all:
+
+ y_val = 0.0
+ for pt in brch.bpoints:
+ if pt.next:
+ y_size = (pt.co-pt.next.co).length
+
+ # scale the uvs by the radiusm, avoids stritching.
+ if do_uv_scalewidth:
+ y_size = y_size / pt.radius
+
+ for i in (0,1,2,3):
+ if pt.faces[i]:
+ if uv_image:
+ pt.faces[i].image = uv_image
+ uvs = pt.faces[i].uv
+ '''
+ uvs[0].x = i
+ uvs[1].x = i
+
+ uvs[2].x = i+1
+ uvs[3].x = i+1
+
+ uvs[1].y = y_val
+ uvs[2].y = y_val
+
+ uvs[0].y = y_val+y_size
+ uvs[3].y = y_val+y_size
+ '''
+ uvs[3].x = i
+ uvs[3].y = y_val+y_size
+
+ uvs[0].x = i
+ uvs[0].y = y_val
+
+ uvs[1].x = i+1
+ uvs[1].y = y_val
+
+ uvs[2].x = i+1
+ uvs[2].y = y_val+y_size
+
+
+ do_uv_scalewidth
+ if pt.next:
+ y_val += y_size
+
+
+ return self.mesh
+
+zup = Vector(0,0,1)
+
+class bpoint:
+ ''' The point in the middle of the branch, not the mesh points
+ '''
+ def __init__(self, brch, co, no, radius):
+ self.branch = brch
+ self.co = co
+ self.no = no
+ self.radius = radius
+ self.vecs = [None, None, None, None] # 4 for now
+ self.verts = [None, None, None, None]
+ self.children = [None, None, None, None] # child branches, dont fill in faces here
+ self.faces = [None, None, None, None]
+ self.next = None
+ self.prev = None
+
+
+ # when set, This is the angle we need to roll to best face our branches
+ # the roll that is set may be interpolated if we are between 2 branches that need to roll.
+ # Set to None means that the roll will be left default (from parent)
+ self.roll_angle = None
+
+
+ # The location between this and the next point,
+ # if we want to be tricky we can try make this not just a simple
+ # inbetween and use the normals to have some curvature
+ self.nextMidCo = None
+
+ # Similar to above, median point of all children
+ self.childrenMidCo = None
+
+ # Similar as above, but for radius
+ self.childrenMidRadius = None
+
+ # Target locations are used when you want to move the point to a new location but there are
+ # more then 1 influence, build up a list and then apply
+ self.targetCos = []
+
+ def setCo(self, co):
+ self.co[:] = co
+ self.calcNextMidCo()
+ self.calcNormal()
+
+ if self.prev:
+ self.prev.calcNextMidCo()
+ self.prev.calcNormal()
+ self.prev.calcChildrenMidData()
+
+ if self.next:
+ self.prev.calcNormal()
+
+ self.calcChildrenMidData()
+
+
+ def nextLength(self):
+ return (self.co-self.next.co).length
+ def prevLength(self):
+ return (self.co-self.prev.co).length
+
+ def hasOverlapError(self):
+ if self.prev == None:
+ return False
+ if self.next == None:
+ return False
+ '''
+ # see if this point sits on the line between its siblings.
+ co, fac = ClosestPointOnLine(self.co, self.prev.co, self.next.co)
+
+ if fac >= 0.0 and fac <= 1.0:
+ return False # no overlap, we are good
+ else:
+ return True # error, some overlap
+ '''
+
+
+ # Alternate method, maybe better
+ ln = self.nextLength()
+ lp = self.prevLength()
+ ls = (self.prev.co-self.next.co).length
+
+ # Are we overlapping? the length from our next or prev is longer then the next-TO-previous?
+ if ln>ls or lp>ls:
+ return True
+ else:
+ return False
+
+
+ def applyTargetLocation(self):
+ if not self.targetCos:
+ return False
+ elif len(self.targetCos) == 1:
+ self.setCo(self.targetCos[0])
+ else:
+ co_all = Vector()
+ for co in self.targetCos:
+ co_all += co
+
+ self.setCo(co_all / len(self.targetCos))
+ self.targetCos[:] = []
+ return True
+
+ def calcNextMidCo(self):
+ if not self.next:
+ return None
+
+ # be tricky later.
+ self.nextMidCo = (self.co + self.next.co) * 0.5
+
+ def calcNormal(self):
+ if self.prev == None:
+ self.no = (self.next.co - self.co).normalize()
+ elif self.next == None:
+ self.no = (self.co - self.prev.co).normalize()
+ else:
+ self.no = (self.next.co - self.prev.co).normalize()
+
+ def calcChildrenMidData(self):
+ '''
+ Calculate childrenMidCo & childrenMidRadius
+ This is a bit tricky, we need to find a point between this and the next,
+ the medium of all children, this point will be on the line between this and the next.
+ '''
+ if not self.next:
+ return None
+
+ # factor between this and the next point
+ radius = factor = factor_i = 0.0
+
+ count = 0
+ for brch in self.children:
+ if brch: # we dont need the co at teh moment.
+ co, fac = ClosestPointOnLine(brch.bpoints[0].co, self.co, self.next.co)
+ factor_i += fac
+ count += 1
+
+ radius += brch.bpoints[0].radius
+
+ if not count:
+ return
+
+ # interpolate points
+ factor_i = factor_i/count
+ factor = 1-factor_i
+
+ self.childrenMidCo = (self.co * factor) + (self.next.co * factor_i)
+ self.childrenMidRadius = radius
+
+ #debug_pt(self.childrenMidCo)
+
+ def getAbsVec(self, index):
+ # print self.vecs, index
+ return self.co + self.vecs[index]
+
+ def slide(self, factor):
+ '''
+ Slides the segment up and down using the prev and next points
+ '''
+ self.setCo(self.slideCo(factor))
+
+ def slideCo(self, factor):
+ if self.prev == None or self.next == None or factor==0.0:
+ return
+
+ if factor < 0.0:
+ prev_co = self.prev.co
+ co = self.co
+
+ ofs = co-prev_co
+ ofs.length = abs(factor)
+ self.co - ofs
+
+ return self.co - ofs
+ else:
+ next_co = self.next.co
+ co = self.co
+
+ ofs = co-next_co
+ ofs.length = abs(factor)
+
+ return self.co - ofs
+
+
+ def collapseDown(self, branch):
+ '''
+ Collapse the next point into this one
+ '''
+
+ # self.next.next == None check is so we dont shorten the final length of branches.
+ if self.next == None or self.next.next == None or self.hasChildren() or self.next.hasChildren():
+ return False
+
+ branch.bpoints.remove(self.next)
+ self.next = self.next.next # skip
+ self.next.prev = self
+
+ # Watch this place - must update all data thats needed. roll is not calculaetd yet.
+ self.calcNextMidCo()
+ return True
+
+ def collapseUp(self, branch):
+ '''
+ Collapse the previous point into this one
+ '''
+
+ # self.next.next == None check is so we dont shorten the final length of branches.
+ if self.prev == None or self.prev.prev == None or self.prev.hasChildren() or self.prev.prev.hasChildren():
+ return False
+
+ branch.bpoints.remove(self.prev)
+ self.prev = self.prev.prev # skip
+ self.prev.next = self
+
+ # Watch this place - must update all data thats needed. roll is not calculaetd yet.
+ self.prev.calcNextMidCo()
+ return True
+
+
+ def smooth(self, factor):
+ '''
+ Blend this point into the other 2 points
+ '''
+
+ #if self.next == None or self.prev == None or self.hasChildren() or self.prev.hasChildren():
+ if self.next == None or self.prev == None:
+ return False
+
+ radius = (self.next.radius + self.prev.radius)/2.0
+ no = (self.next.no + self.prev.no).normalize()
+
+ # do a line intersect to work out the best location
+ '''
+ cos = LineIntersect( self.next.co, self.next.co+self.next.no,\
+ self.prev.co, self.prev.co+self.prev.no)
+ if cos == None:
+ co = (self.prev.co + self.next.co)/2.0
+ else:
+ co = (cos[0]+cos[1])/2.0
+ '''
+ # Above can give odd results every now and then
+ co = (self.prev.co + self.next.co)/2.0
+
+ # Now apply
+ factor_i = 1.0-factor
+ self.setCo(self.co*factor_i + co*factor)
+ self.radius = self.radius*factor_i + radius*factor
+
+ return True
+
+ def childPoint(self, index):
+ '''
+ Returns the middle point for any children between this and the next edge
+ '''
+ if self.next == None:
+ raise 'Error'
+
+ if index == 0: return (self.getAbsVec(0) + self.next.getAbsVec(1)) / 2
+ if index == 1: return (self.getAbsVec(1) + self.next.getAbsVec(2)) / 2
+ if index == 2: return (self.getAbsVec(2) + self.next.getAbsVec(3)) / 2
+ if index == 3: return (self.getAbsVec(3) + self.next.getAbsVec(0)) / 2
+
+ def roll(self, angle):
+ '''
+ Roll the quad about its normal
+ use for aurienting the sides of a quad to meet a branch that stems from here...
+ '''
+
+ mat = Blender.Mathutils.RotationMatrix(angle, 3, 'r', self.no)
+ for i in xrange(4):
+ self.vecs[i] = self.vecs[i] * mat
+
+
+ def toMesh(self, mesh):
+ self.verts[0].co = self.getAbsVec(0)
+ self.verts[1].co = self.getAbsVec(1)
+ self.verts[2].co = self.getAbsVec(2)
+ self.verts[3].co = self.getAbsVec(3)
+
+ if not self.next:
+ return
+ verts = self.verts
+ next_verts = self.next.verts
+
+ ls = []
+ if self.prev == None and self.branch.parent_pt:
+ # join from parent branch
+
+ # which side are we of the parents quad
+ index = self.branch.parent_pt.children.index(self.branch)
+
+ if index==0: verts = [self.branch.parent_pt.verts[0], self.branch.parent_pt.verts[1], self.branch.parent_pt.next.verts[1], self.branch.parent_pt.next.verts[0]]
+ if index==1: verts = [self.branch.parent_pt.verts[1], self.branch.parent_pt.verts[2], self.branch.parent_pt.next.verts[2], self.branch.parent_pt.next.verts[1]]
+ if index==2: verts = [self.branch.parent_pt.verts[2], self.branch.parent_pt.verts[3], self.branch.parent_pt.next.verts[3], self.branch.parent_pt.next.verts[2]]
+ if index==3: verts = [self.branch.parent_pt.verts[3], self.branch.parent_pt.verts[0], self.branch.parent_pt.next.verts[0], self.branch.parent_pt.next.verts[3]]
+
+ if not self.children[0]: self.faces[0] = [verts[0], verts[1], next_verts[1], next_verts[0]]
+ if not self.children[1]: self.faces[1] = [verts[1], verts[2], next_verts[2], next_verts[1]]
+ if not self.children[2]: self.faces[2] = [verts[2], verts[3], next_verts[3], next_verts[2]]
+ if not self.children[3]: self.faces[3] = [verts[3], verts[0], next_verts[0], next_verts[3]]
+
+ else:
+ # normal join
+ if not self.children[0]: self.faces[0] = [verts[0], verts[1], next_verts[1], next_verts[0]]
+ if not self.children[1]: self.faces[1] = [verts[1], verts[2], next_verts[2], next_verts[1]]
+ if not self.children[2]: self.faces[2] = [verts[2], verts[3], next_verts[3], next_verts[2]]
+ if not self.children[3]: self.faces[3] = [verts[3], verts[0], next_verts[0], next_verts[3]]
+
+ mesh.faces.extend(ls)
+
+ def calcVerts(self):
+ # place the 4 verts we have assigned.
+ #for i, v in self.verts:
+ # v.co = self.co
+
+ if self.prev == None:
+ if self.branch.parent_pt:
+ #cross = CrossVecs(brch.parent_pt.vecs[3], self.no)
+
+ # TOD - if branch insets the trunk
+ ### cross = CrossVecs(self.no, brch.getParentFaceCent() - brch.parent_pt.getAbsVec( brch.getParentQuadIndex() ))
+ # cross = CrossVecs(self.no, self.)
+
+
+ #debug_pt( brch.parent_pt.getAbsVec( brch.getParentQuadIndex() ))
+ #debug_pt( brch.getParentFaceCent() )
+ #debug_pt( brch.parent_pt.getAbsVec( brch.getParentQuadIndex() ))
+ #debug_pt( brch.getParentFaceCent() )
+
+ cross = CrossVecs(self.no, self.branch.getParentFaceCent() - self.branch.parent_pt.getAbsVec( self.branch.getParentQuadIndex() ))
+
+
+ else:
+ # parentless branch
+ cross = zup
+ else:
+ cross = CrossVecs(self.prev.vecs[0], self.no)
+
+ self.vecs[0] = Blender.Mathutils.CrossVecs(self.no, cross)
+ self.vecs[0].length = self.radius
+ mat = Blender.Mathutils.RotationMatrix(90, 3, 'r', self.no)
+ self.vecs[1] = self.vecs[0] * mat
+ self.vecs[2] = self.vecs[1] * mat
+ self.vecs[3] = self.vecs[2] * mat
+
+
+ '''
+ Blender.Window.SetCursorPos(tuple(v.co))
+ Blender.Window.RedrawAll()
+
+ while True:
+ val = Blender.Window.QRead()[1]
+ if val:
+ break
+
+ v.co += (self.no * (self.radius * 0.01))
+ '''
+
+ def hasChildren(self):
+ if self.children[0] != None or\
+ self.children[1] != None or\
+ self.children[2] != None or\
+ self.children[3] != None:
+ return True
+ else:
+ return False
+
+class branch:
+ def __init__(self):
+ self.bpoints = []
+ self.parent_pt = None
+ self.tag = False # have we calculated our points
+
+ # Bones per branch
+ self.bones = []
+
+ def getParentQuadAngle(self):
+ '''
+ The angle off we are from our parent quad,
+ '''
+ # used to roll the parent so its faces us better
+ parent_normal = self.getParentFaceCent() - self.parent_pt.nextMidCo
+ self_normal = self.bpoints[1].co - self.parent_pt.co
+ # We only want the angle in relation to the parent points normal
+ # modify self_normal to make this so
+ cross = CrossVecs(self_normal, self.parent_pt.no)
+ self_normal = CrossVecs(self.parent_pt.no, cross) # CHECK
+ angle = AngleBetweenVecs(parent_normal, self_normal)
+
+ # see if we need to rotate positive or negative
+ # USE DOT PRODUCT!
+ cross = CrossVecs(parent_normal, self_normal)
+ if AngleBetweenVecs(cross, self.parent_pt.no) > 90:
+ angle = -angle
+
+ return angle
+
+ def getParentQuadIndex(self):
+ return self.parent_pt.children.index(self)
+ def getParentFaceCent(self):
+ return self.parent_pt.childPoint( self.getParentQuadIndex() )
+
+ def findClosest(self, co):
+ ''' # this dosnt work, but could.
+ best = None
+ best_dist = 100000000
+ for pt in self.bpoints:
+ if pt.next:
+ co_on_line, fac = ClosestPointOnLine(co, pt.co, pt.next.co)
+ print fac
+ if fac >= 0.0 and fac <= 1.0:
+ return pt, (co-co_on_line).length
+
+ return best, best_dist
+ '''
+ best = None
+ best_dist = 100000000
+ for pt in self.bpoints:
+ if pt.nextMidCo:
+ dist = (pt.nextMidCo-co).length
+ if dist < best_dist:
+ best = pt
+ best_dist = dist
+
+ return best, best_dist
+
+ def evenPointDistrobution(self, factor):
+ '''
+ Redistribute points that are not evenly distributed
+ factor is between 0.0 and 1.0
+ '''
+
+ for pt in self.bpoints:
+ if pt.next and pt.prev and pt.hasChildren() == False and pt.prev.hasChildren() == False:
+ w1 = pt.nextLength()
+ w2 = pt.prevLength()
+ wtot = w1+w2
+ w1=w1/wtot
+ #w2=w2/wtot
+ w1 = abs(w1-0.5)*2 # make this from 0.0 to 1.0, where 0 is the middle and 1.0 is as far out of the middle as possible.
+ # print "%.6f" % w1
+ pt.smooth(w1*factor)
+
+ def fixOverlapError(self):
+ # Keep fixing until no hasOverlapError left to fix.
+ error = True
+ while error:
+ error = False
+ for pt in self.bpoints:
+ #if pt.prev and pt.hasChildren() == False and pt.prev.hasChildren() == False:
+ if pt.prev and pt.next:
+ if pt.hasOverlapError():
+ error = True
+ pt.smooth(1.0)
+
+ def evenJointDistrobution(self, joint_compression = 1.0):
+ # See if we need to evaluate this branch at all
+ if len(self.bpoints) <= 2: # Rare but in this case we cant do anything
+ return
+ has_children = False
+ for pt in self.bpoints:
+ if pt.hasChildren():
+ has_children = True
+ break
+
+ if not has_children:
+ return
+
+ # OK, we have children, so we have some work to do...
+ # center each segment
+
+ # work out the median location of all points children.
+ for pt in self.bpoints:
+ pt.calcChildrenMidData()
+
+ for pt in self.bpoints:
+ pt.targetCos = []
+ if pt.childrenMidCo:
+ # Move this and the next segment to be around the child point.
+ # TODO - collect slides and then average- only happens sometimes so not that bad.
+ # TODO - factor in the branch angle, be careful with this - close angles can have extreme values.
+ co = pt.slideCo( (pt.childrenMidCo - pt.co).length - (pt.childrenMidRadius * joint_compression) )
+ if co:
+ pt.targetCos.append( co )
+
+ co = pt.next.slideCo((pt.childrenMidRadius * joint_compression) - (pt.childrenMidCo - pt.next.co).length )
+ if co:
+ pt.next.targetCos.append( co )
+
+ for pt in self.bpoints:
+ pt.applyTargetLocation()
+
+ def collapsePoints(self, density):
+ collapse = True
+ while collapse:
+ collapse = False
+
+ pt = self.bpoints[0]
+ while pt:
+
+ if pt.prev and pt.next and not pt.prev.hasChildren():
+ if (pt.prev.nextMidCo-pt.co).length < ((pt.radius + pt.prev.radius)/2) * density:
+ pt_save = pt.prev
+ if pt.next.collapseUp(self): # collapse this point
+ collapse = True
+ pt = pt_save # so we never reference a removed point
+
+ if not pt.hasChildren(): #if pt.childrenMidCo == None:
+ # Collapse, if tehre is any problems here we can move into a seperate losop.
+ # do here because we only want to run this on points with no childzren,
+
+ # Are we closer theto eachother then the radius?
+ if pt.next and (pt.nextMidCo-pt.co).length < ((pt.radius + pt.next.radius)/2) * density:
+ if pt.collapseDown(self):
+ collapse = True
+
+ pt = pt.next
+ ## self.checkPointList()
+ self.evenPointDistrobution(1.0)
+
+ for pt in self.bpoints:
+ pt.calcNormal()
+ pt.calcNextMidCo()
+
+ def branchReJoin(self):
+ '''
+ Not needed but nice to run after collapsing incase segments moved a lot
+ '''
+ if not self.parent_pt:
+ return # nothing to do
+
+ # see if the next segment is closer now (after collapsing)
+ par_pt = self.parent_pt
+ root_pt = self.bpoints[0]
+
+ index = par_pt.children.index(self)
+
+ current_dist = (par_pt.nextMidCo - root_pt.co).length
+
+ # TODO - Check size of new area is ok to move into
+
+ if par_pt.next and par_pt.next.next and par_pt.next.children[index] == None:
+ # We can go here if we want, see if its better
+ if current_dist > (par_pt.next.nextMidCo - root_pt.co).length:
+ self.parent_pt.children[index] = None
+ self.parent_pt = par_pt.next
+ self.parent_pt.children[index] = self
+ return
+
+ if par_pt.prev and par_pt.prev.children[index] == None:
+ # We can go here if we want, see if its better
+ if current_dist > (par_pt.prev.nextMidCo - root_pt.co).length:
+ self.parent_pt.children[index] = None
+ self.parent_pt = par_pt.prev
+ self.parent_pt.children[index] = self
+ return
+
+ def checkPointList(self):
+ '''
+ Error checking. use to check if collapsing worked.
+ '''
+ p_link = self.bpoints[0]
+ i = 0
+ while p_link:
+ if self.bpoints[i] != p_link:
+ raise "Error"
+
+ if p_link.prev and p_link.prev != self.bpoints[i-1]:
+ raise "Error Prev"
+
+ if p_link.next and p_link.next != self.bpoints[i+1]:
+ raise "Error Next"
+
+ p_link = p_link.next
+ i+=1
+
+ def toMesh(self):
+ pass
+
+
+def buildTree(ob):
+ '''
+ Must be a curve object
+ '''
+ t = tree()
+ t.fromCurve(ob)
+ #t.toDebugDisplay()
+ t.buildConnections(sloppy = 1.5, stem_trim = 0.5) # how sloppy to be
+ #t.toDebugDisplay()
+ t.optimizeSpacing(density=0.4, joint_compression=2.8)
+ #t.toDebugDisplay()
+ mesh = Blender.Mesh.Get('Mesh')
+ mesh = t.toMesh(mesh, uv_image= Blender.Image.Get('bark'), do_uv_scalewidth = False)
+ #t.toDebugDisplay()
+
+ #armature = t.toArmature()
+ if 0:
+ ob_mesh = bpy.data.scenes.active.objects.new(mesh)
+ ob_mesh.setMatrix(ob.matrixWorld)
+ ob_mesh.sel = 0
+
+
+
+# this should run as a module
+if __name__ == '__main__':
+ sce = bpy.data.scenes.active
+ ob = sce.objects.active
+ #ob = bpy.data.objects['Curve']
+ buildTree(ob)