1from pykludge3d import *
2from lsystem import *
3
4"""
5Note: I use the term 'face' a lot in this file.  In this situation, 'face' is a
6synonym for 'polygon'.  I use 'face' instead of 'polygon' because, when I say
7'face', I'm also referring to the L-system action/symbol involving polygons.
8
9L-system action symbols:
10
11S			scale the face
12M			move the face along the face's normal
13E[...]		extrude the face and branch the L-system.  Each item in the
14			comma-separated list between the brackets is a branch.  The first
15			branch is the branch for the original face (ie the face that,
16			following the extrude, is facing in the same direction as the face
17			that was extruded).  The subsequent branches are for the N other
18			faces resulting from the extrude.  You do not need to specify an
19			action for all N+1 of the branches; use 'T' for branches you don't
20			care about.  If there are more items in the list than faces
21			resulting from the extrude, the extra items are ignored.  If there
22			are fewer items in the list than faces resulting from the extrude,
23			the extra faces will not be acted on (the same as if 'T' were
24			specified).
25			After the extrude action itself has been performed, the branches
26			will be executed in a depth-first manner, starting with the first
27			branch (the one corresponding to the original face).
28			Action symbols following the E and its braces will be executed
29			after all symbols in the E have been acted upon.
30			Note that the brackets are optional.  If you do not wish to
31			manipulate the new faces resulting from an extrude, simply use E
32			by itself.
33T			terminal.  A no-op; no action is performed on the face.
340-9			digits are ignored
35"""
36
37def scale_face( currentFace, depth ):
38	scaleFactor = 0.75
39	scale_verts( get_verts_in_polys( [currentFace] ), scaleFactor, scaleFactor, scaleFactor )
40
41def move_face( currentFace, depth ):
42	moveFactor = 1.0
43	normal = get_poly_normal( currentFace, None )
44	#print normal
45	translate_verts( get_verts_in_polys( [currentFace] ),
46					normal[0] * moveFactor, normal[1] * moveFactor,
47					normal[2] * moveFactor )
48
49def extrude_face( currentFace, depth ):
50	return polys_extrude( [currentFace], 0 )
51
52def get_bracketed_substring( string, startIndex ):
53	if string[startIndex] != '[':
54		return ''
55	rbcount = 0 # right-bracket count
56	i = startIndex+1
57	while rbcount != 1 and i < len(string):
58		if string[i] == '[':
59			rbcount -= 1
60		if string[i] == ']':
61			rbcount += 1
62		i += 1
63	if rbcount != 1:
64		return ''
65	return string[startIndex+1 : i-1]
66# end get_bracketed_substring
67
68def interpret_branches( faces, branchString, depth ):
69	i = 0
70	faceNum = 0
71	while i < len( branchString ):
72		temp = ''
73		while i < len( branchString ) and branchString[i] != ',':
74			if branchString[i] == '[':
75				temp += branchString[i]
76				i += 1
77				# we want to skip over everything between the braces.
78				# advance i until matching bracket is found
79				rbcount = 0 # right-bracket count
80				while rbcount != 1 and i < len(branchString):
81					if branchString[i] == '[':
82						rbcount -= 1
83					if branchString[i] == ']':
84						rbcount += 1
85					temp += branchString[i]
86					i += 1
87			else:
88				temp += branchString[i]
89				i += 1
90		if i < len( branchString ) and branchString[i] == ',':
91			i += 1
92		#print '\t'*depth, 'temp is ', temp
93		#print '\t'*depth, 'i is ', i
94		interpret_action( temp, faces[faceNum], depth+1 )
95		faceNum += 1
96# end interpret_branches
97
98def interpret_action( actionString, currentFace, depth=1 ):
99	i = 0
100	while i < len(actionString):
101		action = actionString[i]
102		if action == 'T' or action.isdigit() or action.isspace():
103			print 'terminal/no-op'
104		elif action == 'S':
105			scale_face( currentFace, depth )
106		elif action == 'M':
107			move_face( currentFace, depth )
108		elif action == 'E':
109			# extrude
110			faces = extrude_face( currentFace, depth )
111			# follow the branches
112			branchString = get_bracketed_substring( actionString, i+1 )
113			interpret_branches( faces, branchString, depth )
114		elif action == '[':
115			# we want to skip over everything between the braces.
116			# advance i until matching bracket is found
117			rbcount = 0 # right-bracket count
118			while rbcount != 1 and i < len(actionString):
119				if actionString[i] == '[':
120					rbcount -= 1
121				if actionString[i] == ']':
122					rbcount += 1
123				i += 1
124		else:
125			print 'Unknown action', action
126		i += 1
127# end interpret_action
128
129
130def run_lsystem( axiom = '0', rules = [('0', 'EM0')], generations = 5 ):
131	'''Runs an L-system (Lindenmeyer system), using the given initial string
132	(the axiom) and the set of rules for string-substitution.  Once the
133	L-system has been run for the specified number of generations, the
134	resulting string will be interpreted (see below).
135
136	How to pass rules into the L-system:
137	The 'rules' list is a list of tuples, each tuple being a rule for string
138	substitution.  Each rule tuple has two parts, a single character to look
139	for, and a string to replace the character with.  Here's an example:
140
141	('0', 'EM0')
142
143	This rule means that every time '0' is encountered, it is replaced with
144	the string 'EM0'.  If our initial string is '0', and we apply this rule
145	for, say, 5 generations, we end up with the following progression:
146
147	0 -> EM0 -> EMEM0 -> EMEMEM0 -> EMEMEMEM0 -> EMEMEMEMEM0
148
149	How the result strings are interpreted:
150	A complete description of the symbols can be found in the doc string for
151	this module, but here is a brief summary:
152	S			scale the face
153	M			move the face along the face's normal
154	E[...]		extrude the face and branch the L-system (definitely see the
155				doc string for more info)
156	T			ignored
157	0-9			ignored (used to represent faces at the string-substitution
158				stage, useless once string-substitution is complete)
159	'''
160	polys = get_selected_polys()
161
162	if len( polys ) < 1:
163		print 'At least one polygon must be selected'
164		return
165
166	system = LSystem( axiom, rules )
167	while not system.done and system.generation < generations:
168		system.step()
169	interpret_action( system.string, polys[0] )
170# end run_lsystem
171
172
173