[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