[Bf-extensions-cvs] SVN commit: /data/svn/bf-extensions [4637] contrib/py/scripts/addons: Initial commit.
Michel Anders
michel.anders at inter.nl.net
Wed Jul 24 12:37:37 CEST 2013
Revision: 4637
http://projects.blender.org/scm/viewvc.php?view=rev&root=bf-extensions&revision=4637
Author: varkenvarken
Date: 2013-07-24 10:37:37 +0000 (Wed, 24 Jul 2013)
Log Message:
-----------
Initial commit.
Identical to the 0.0.8 release on GitHub https://github.com/varkenvarken/spacetree/tree/master/release
add_mesh_space_tree is a tree generation add-on based on the space colonization algorithm by A. Runions.
It is aimed as an alternative (not a replacement) to Sapling.
currently full docs are at http://blenderthings.blogspot.nl/p/the-spacetree-blender-add-on.html but these will be moved to the BlenderWiki ar
http://wiki.blender.org/index.php/Extensions:2.6/Py/Scripts/Add_Mesh/Add_Space_Tree
Next steps: transferring documentation to wiki, updating bl_info, and pep8 compliance.
Added Paths:
-----------
contrib/py/scripts/addons/add_mesh_space_tree/
contrib/py/scripts/addons/add_mesh_space_tree/__init__.py
contrib/py/scripts/addons/add_mesh_space_tree/kdtree.py
contrib/py/scripts/addons/add_mesh_space_tree/sca.py
contrib/py/scripts/addons/add_mesh_space_tree/simplefork.py
contrib/py/scripts/addons/add_mesh_space_tree/timer.py
Property changes on: contrib/py/scripts/addons/add_mesh_space_tree
___________________________________________________________________
Added: tsvn:logminsize
+ 10
Added: contrib/py/scripts/addons/add_mesh_space_tree/__init__.py
===================================================================
--- contrib/py/scripts/addons/add_mesh_space_tree/__init__.py (rev 0)
+++ contrib/py/scripts/addons/add_mesh_space_tree/__init__.py 2013-07-24 10:37:37 UTC (rev 4637)
@@ -0,0 +1,940 @@
+# ##### BEGIN GPL LICENSE BLOCK #####
+#
+# SCA Tree Generator, a Blender addon
+# (c) 2013 Michel J. Anders (varkenvarken)
+#
+# 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 #####
+
+bl_info = {
+ "name": "SCA Tree Generator",
+ "author": "michel anders (varkenvarken)",
+ "version": (0, 0, 8),
+ "blender": (2, 66, 0),
+ "location": "View3D > Add > Mesh",
+ "description": "Adds a tree created with the space colonization algorithm starting at the 3D cursor",
+ "warning": "",
+ "wiki_url": "https://github.com/varkenvarken/spacetree/wiki",
+ "tracker_url": "",
+ "category": "Add Mesh"}
+
+from time import time
+from random import random,gauss
+from functools import partial
+from math import sin,cos
+
+import bpy
+from bpy.props import FloatProperty, IntProperty, BoolProperty, EnumProperty
+from mathutils import Vector,Euler,Matrix,Quaternion
+
+from .simplefork import simplefork, simplefork2, quadfork, bridgequads # simple skinning algorithm building blocks
+from .sca import SCA, Branchpoint # the core class that implements the space colonization algorithm and the definition of a segment
+from .timer import Timer
+
+def availableGroups(self, context):
+ return [(name, name, name, n) for n,name in enumerate(bpy.data.groups.keys())]
+
+def availableGroupsOrNone(self, context):
+ groups = [ ('None', 'None', 'None', 1) ]
+ return groups + [(name, name, name, n+1) for n,name in enumerate(bpy.data.groups.keys())]
+
+def availableObjects(self, context):
+ return [(name, name, name, n+1) for n,name in enumerate(bpy.data.objects.keys())]
+
+def ellipsoid(r=5,rz=5,p=Vector((0,0,8)),taper=0):
+ r2=r*r
+ z2=rz*rz
+ if rz>r : r = rz
+ while True:
+ x = (random()*2-1)*r
+ y = (random()*2-1)*r
+ z = (random()*2-1)*r
+ f = (z+r)/(2*r)
+ f = 1 + f*taper if taper>=0 else (1-f)*-taper
+ if f*x*x/r2+f*y*y/r2+z*z/z2 <= 1:
+ yield p+Vector((x,y,z))
+
+def pointInsideMesh(pointrelativetocursor,ob):
+ # adapted from http://blenderartists.org/forum/showthread.php?195605-Detecting-if-a-point-is-inside-a-mesh-2-5-API&p=1691633&viewfull=1#post1691633
+ mat = ob.matrix_world.inverted()
+ orig = mat*(pointrelativetocursor+bpy.context.scene.cursor_location)
+ count = 0
+ axis=Vector((0,0,1))
+ while True:
+ location,normal,index = ob.ray_cast(orig,orig+axis*10000.0)
+ if index == -1: break
+ count += 1
+ orig = location + axis*0.00001
+ if count%2 == 0:
+ return False
+ return True
+
+def ellipsoid2(rxy=5,rz=5,p=Vector((0,0,8)),surfacebias=1,topbias=1):
+ while True:
+ phi = 6.283*random()
+ theta = 3.1415*(random()-0.5)
+ r = random()**(surfacebias/2)
+ x = r*rxy*cos(theta)*cos(phi)
+ y = r*rxy*cos(theta)*sin(phi)
+ st=sin(theta)
+ st = (((st+1)/2)**topbias)*2-1
+ z = r*rz*st
+ #print(">>>%.2f %.2f %.2f "%(x,y,z))
+ m = p+Vector((x,y,z))
+ reject = False
+ for ob in bpy.context.selected_objects:
+ # probably we should check if each object is a mesh
+ if pointInsideMesh(m,ob) :
+ reject = True
+ break
+ if not reject:
+ yield m
+
+def halton3D(index):
+ """
+ return a quasi random 3D vector R3 in [0,1].
+ each component is based on a halton sequence.
+ quasi random is good enough for our purposes and is
+ more evenly distributed then pseudo random sequences.
+ See en.m.wikipedia.org/wiki/Halton_sequence
+ """
+
+ def halton(index, base):
+ result=0
+ f=1.0/base
+ I=index
+ while I>0:
+ result += f*(I%base)
+ I=int(I/base)
+ f/=base
+ return result
+ return Vector((halton(index,2),halton(index,3),halton(index,5)))
+
+def insidegroup(pointrelativetocursor, group):
+ if bpy.data.groups.find(group)<0 : return False
+ for ob in bpy.data.groups[group].objects:
+ if pointInsideMesh(pointrelativetocursor,ob):
+ return True
+ return False
+
+def groupdistribution(crowngroup,shadowgroup=None,seed=0,size=Vector((1,1,1)),pointrelativetocursor=Vector((0,0,0))):
+ if crowngroup == shadowgroup:
+ shadowgroup = None # safeguard otherwise every marker would be rejected
+ nocrowngroup = bpy.data.groups.find(crowngroup)<0
+ noshadowgroup = (shadowgroup is None) or (bpy.data.groups.find(shadowgroup)<0) or (shadowgroup == 'None')
+ index=100+seed
+ nmarkers=0
+ nyield=0
+ while True:
+ nmarkers+=1
+ v = halton3D(index)
+ v[0] *= size[0]
+ v[1] *= size[1]
+ v[2] *= size[2]
+ v+=pointrelativetocursor
+ index+=1
+ insidecrown = nocrowngroup or insidegroup(v,crowngroup)
+ outsideshadow = noshadowgroup or not insidegroup(v,shadowgroup)
+ # if shadowgroup overlaps all or a significant part of the crowngroup
+ # no markers will be yielded and we would be in an endless loop.
+ # so if we yield too few correct markers we start yielding them anyway.
+ lowyieldrate = (nmarkers>200) and (nyield/nmarkers < 0.01)
+ if (insidecrown and outsideshadow) or lowyieldrate:
+ nyield+=1
+ yield v
+
+def groupExtends(group):
+ """
+ return a size,minimum tuple both Vector elements, describing the size and position
+ of the bounding box in world space that encapsulates all objects in a group.
+ """
+ bb=[]
+ if bpy.data.groups.find(group) >=0 :
+ for ob in bpy.data.groups[group].objects:
+ rot = ob.matrix_world.to_quaternion()
+ scale = ob.matrix_world.to_scale()
+ translate = ob.matrix_world.translation
+ for v in ob.bound_box: # v is not a vector but an array of floats
+ p = ob.matrix_world * Vector(v[0:3])
+ bb.extend(p[0:3])
+ mx = Vector((max(bb[0::3]), max(bb[1::3]), max(bb[2::3])))
+ mn = Vector((min(bb[0::3]), min(bb[1::3]), min(bb[2::3])))
+ return mx-mn,mn
+
+def createLeaves(tree, probability=0.5, size=0.5, randomsize=0.1, randomrot=0.1, maxconnections=2, bunchiness=1.0):
+ p=bpy.context.scene.cursor_location
+
+ verts=[]
+ faces=[]
+ c1=Vector((-size/10,-size/2,0))
+ c2=Vector(( size,-size/2,0))
+ c3=Vector(( size, size/2,0))
+ c4=Vector((-size/10, size/2,0))
+ t=gauss(1.0/probability,0.1)
+ bpswithleaves=0
+ for bp in tree.branchpoints:
+ if bp.connections < maxconnections:
+
+ dv = tree.branchpoints[bp.parent].v - bp.v if bp.parent else Vector((0,0,0))
+ dvp = Vector((0,0,0))
+
+ bpswithleaves+=1
+ nleavesonbp=0
+ while t<bpswithleaves:
+ nleavesonbp+=1
+ rx = (random()-0.5)*randomrot*6.283 # TODO vertical tilt in direction of tropism
+ ry = (random()-0.5)*randomrot*6.283
+ rot = Euler((rx,ry,random()*6.283),'ZXY')
+ scale = 1+(random()-0.5)*randomsize
+ v=c1.copy()
+ v.rotate(rot)
+ verts.append(v*scale+bp.v+dvp)
+ v=c2.copy()
+ v.rotate(rot)
+ verts.append(v*scale+bp.v+dvp)
+ v=c3.copy()
+ v.rotate(rot)
+ verts.append(v*scale+bp.v+dvp)
+ v=c4.copy()
+ v.rotate(rot)
+ verts.append(v*scale+bp.v+dvp)
+ n = len(verts)
+ faces.append((n-4,n-3,n-2,n-1))
+ t += gauss(1.0/probability,0.1) # this is not the best choice of distribution because we might get negative values especially if sigma is large
+ dvp = nleavesonbp*(dv/(probability**bunchiness)) # TODO add some randomness to the offset
+
+ mesh = bpy.data.meshes.new('Leaves')
+ mesh.from_pydata(verts,[],faces)
+ mesh.update(calc_edges=True)
+ mesh.uv_textures.new()
+ return mesh
+
+def createMarkers(tree,scale=0.05):
+ #not used as markers are parented to tree object that is created at the cursor position
+ #p=bpy.context.scene.cursor_location
+
+ verts=[]
+ faces=[]
+
+ tetraeder = [Vector((-1,1,-1)),Vector((1,-1,-1)),Vector((1,1,1)),Vector((-1,-1,1))]
+ tetraeder = [v * scale for v in tetraeder]
+ tfaces = [(0,1,2),(0,1,3),(1,2,3),(0,3,2)]
+
+ for ep in tree.endpoints:
+ verts.extend([ep + v for v in tetraeder])
+ n=len(faces)
+ faces.extend([(f1+n,f2+n,f3+n) for f1,f2,f3 in tfaces])
+
+ mesh = bpy.data.meshes.new('Markers')
+ mesh.from_pydata(verts,[],faces)
+ mesh.update(calc_edges=True)
+ return mesh
+
+
+def createObjects(tree, parent=None, objectname=None, probability=0.5, size=0.5, randomsize=0.1, randomrot=0.1, maxconnections=2, bunchiness=1.0):
+
+ if (parent is None) or (objectname is None) or (objectname == 'None') : return
+
+ # not necessary, we parent the new objects: p=bpy.context.scene.cursor_location
+
+ theobject = bpy.data.objects[objectname]
+
+ t=gauss(1.0/probability,0.1)
+ bpswithleaves=0
+ for bp in tree.branchpoints:
+ if bp.connections < maxconnections:
+
+ dv = tree.branchpoints[bp.parent].v - bp.v if bp.parent else Vector((0,0,0))
+ dvp = Vector((0,0,0))
+
+ bpswithleaves+=1
+ nleavesonbp=0
+ while t<bpswithleaves:
+ nleavesonbp+=1
+ rx = (random()-0.5)*randomrot*6.283 # TODO vertical tilt in direction of tropism
+ ry = (random()-0.5)*randomrot*6.283
+ rot = Euler((rx,ry,random()*6.283),'ZXY')
+ scale = size+(random()-0.5)*randomsize
+
+ # add new object and parent it
+ obj = bpy.data.objects.new(objectname,theobject.data)
+ obj.location = bp.v+dvp
+ obj.rotation_mode = 'ZXY'
+ obj.rotation_euler = rot[:]
+ obj.scale = [scale,scale,scale]
+ obj.parent = parent
+ bpy.context.scene.objects.link(obj)
+
+ t += gauss(1.0/probability,0.1) # this is not the best choice of distribution because we might get negative values especially if sigma is large
+ dvp = nleavesonbp*(dv/(probability**bunchiness)) # TODO add some randomness to the offset
+
+def vertextend(v,dv):
+ n=len(v)
+ v.extend(dv)
+ return tuple(range(n,n+len(dv)))
+
+def vertcopy(loopa, v, p):
@@ Diff output truncated at 10240 characters. @@
More information about the Bf-extensions-cvs
mailing list