CgTalk Maxscript Challenge 016: "L-Systems!"


#21

Hi every1
Although v. late, but I also wanted to join in the fun.
This is a simple code which displays a 2D representation of the plant. The site (http://algorithmicbotany.org/papers/) pointed by Georg was v. helpful.
Still needs a lot of work (thickness, color, 3D etc) and im working on it.
Any input would be helpful.


 --Plant Generator
 --Author: Bhupendra Aole
 --Dated: 29-Feb-08
 --Version: 1.0
 --Research: http://algorithmicbotany.org/papers/
 --Concept:
 --  Lindenmayer systems — or L-systems for short
 --  The central concept of L-systems is that of rewriting. In general, rewriting
 --  is a technique for defining complex objects by successively replacing
 --  parts of a simple initial object using a set of rewriting rules or productions.
 --Legends:
 --  F: Draw a line of unit length from current position in current direction
 --  +: increase angle
 --  -: decrease angle
 --  [: push the current state (pos, orientation)
 --  ]: pop the last state and make it current
 --Wish list:
 --  1. node writing
 --  2. extend to 3D
 --  3. use colors
 --  4. provide thickness
 --  5. wield vertices
 --Code Version: 0.7
 
 -- simple stack implementation
 struct bstack (
 	s = #(),
 	fn pop = (
 		val = s[s.count]
 		deleteItem s s.count
 		val
 	),
 	fn push val = (
 		append s val
 	)
 )
 
 -- build expression for no. of iterations
 fn buildExp axiom rep iter = (
 	ret = copy axiom -- this is the first iteration itself
 	str = ""
 	for i=2 to iter do ( -- if iterations gr8er than 1
 		for c = 1 to ret.count do ( -- loop thru' the exp
 			if (ret[c] == axiom) then ( -- if rule found
 				append str rep
 			) else ( -- just add the rest
 				append str ret[c]
 			)
 		)
 		ret = copy str
 		str = ""
 	)
 	ret
 )
 
 -- functions for creating spline
 global ss
 fn startLine point = (
 	ss = SplineShape pos:point
 )
 fn addLine pointA pointB =
 (
 	i = addNewSpline ss
 	addKnot ss i #corner #line PointA
 	addKnot ss i #corner #line PointB
 )
 fn endLine = (
 	updateShape ss
 )
 
 -- evaluate expression and draw plant
 fn visualize exp ang = (
 	posStk = bstack ()
 	headStk = bstack ()
 	
 	-- to be UIed
 	pStart = [0,0,0]
 	pLen = 10
 	delta = ang
 	--
 	
 	curPos = copy pStart
 	curAng = 0
 	
 	startLine curPos
 	
 	for c = 1 to exp.count do (
 		if (exp[c] == "F") then (
 			heading = matrix3 1
 			translate heading [0,0,pLen]
 			rotatex heading curAng
 			translate heading curPos
 			nextPos = pStart * heading
 
 			addLine curPos nextPos
 			curPos = nextPos
 		) else if (exp[c] == "+") then (
 			curAng += delta
 		) else if (exp[c] == "-") then (
 			curAng -= delta
 		) else if (exp[c] == "[") then (
 			posStk.push (copy curPos)
 			headStk.push (copy curAng)
 		) else if (exp[c] == "]") then (
 			curPos = posStk.pop ()
 			curAng = headStk.pop ()
 		)
 	)
 	
 	endLine()
 )
 
 -- provide expression and call visualize
 fn buildTree = (
 	-- expressions that produce good results
 	-- F[+F]F[-F]F (25.7)
 	-- F[+F]F[-F][F] (20)
 	-- FF-[-F+F+F]+[+F-F-F] (22.5)
 	
 	-- only use 'F' variable in the expression
 	e = buildExp "F" "F[+F]F[-F]F" 5
 	visualize e 25.5
 )
 
 -- test
 buildTree()
 
 

Thanks,
Bhupendra


#22

IMHO, lovely clean script, clean thinking. Even though it’s ‘late’, I’m looking forward to seeing where it goes!


#23

Hi

thanks Robin!

Extended the code to 3D, but am unhappy with the results.
Cant figure out y the code is not behaving the way it should ... giving somwhat wierd results.

--==================================================================================
--Plant Generator
--Author: Bhupendra Aole
--Dated: 29-Feb-08
--Version: 1.2
--Revision:
--  1.1: 1-mar-8: Implemented Node writing
--  1.2: 4-mar-8: Extend to 3D, added special procedure to create leaves
--Research: http://algorithmicbotany.org/papers/
--Concept:
--  Lindenmayer systems — or L-systems for short
--  The central concept of L-systems is that of rewriting. In general, rewriting
--  is a technique for defining complex objects by successively replacing
--  parts of a simple initial object using a set of rewriting rules or productions.
--Controls:
--  F: Draw a line of unit length from current position in current direction
--  f: same as F w/o drawing
--  +: increase angle (2D)
--  -: decrease angle (2D)
--  [: push the current state (pos, orientation)
--  ]: pop the last state and make it current
--3D controls:
--  + Turn left by angle ƒÂ, using rotation matrix RU(ƒÂ).
--  - Turn right by angle ƒÂ, using rotation matrix RU(.ƒÂ).
--  & Pitch down by angle ƒÂ, using rotation matrix RL(ƒÂ).
--  ^ Pitch up by angle ƒÂ, using rotation matrix RL(.ƒÂ).
--  \ Roll left by angle ƒÂ, using rotation matrix RH(ƒÂ).
--  / Roll right by angle ƒÂ, using rotation matrix RH(.ƒÂ).
--  | Turn around, using rotation matrix RU(180).
--  { start a polygon
--  } complete a polygon
--Wish list:
--  3. use colors
--  4. provide thickness
--  5. wield vertices
--Code Version: 1.23
--Code Revision:
--  1-mar-8: add map implementation
--	 "   : changed buildExp to handle map (more than 1 var)
--  4-mar-8: added 3d angles for movement
--	   "   : createPoly() creates "capped" surfaces bounded by vertices
--  5-mar-8: added extrude modifier to leaf before cap_holes modifier
--==================================================================================
-- simple map implementation
struct keyvalue (
	k = #(),
	v = #()
)
struct bMap (
	entry = #(),
	fn put key value = (
		kv = keyvalue k:key v:value
		append entry kv
	),
	fn get key = (
		ret = undefined
		for i=1 to entry.count do (
			kv = entry[i]
			if (kv.k == key) then (
				ret = kv.v
				exit
			)
		)
		ret
	)
)

-- simple stack implementation
struct bStack (
	s = #(),
	fn pop = (
		val = s[s.count]
		deleteItem s s.count
		val
	),
	fn push val = (
		append s val
	),
	fn empty = (
		s = #()
	),
	fn size = (
		s.count
	)
)

-- build expression for no. of iterations
fn buildExp axiom rules iter = (
	ret = copy axiom -- this is the first iteration itself
	str = ""
	for i=2 to iter do ( -- if iterations gr8er than 1
		for c = 1 to ret.count do ( -- loop thru' the exp
			val = rules.get ret[c]
			if (val == undefined) then (
				-- special chars ... just append
				append str ret[c]
			) else (
				append str val
			)
		)
		ret = copy str
		str = ""
	)
	ret
)

-- functions for creating spline
global ss
fn startLine point = (
	ss = SplineShape pos:point
)
fn addLine pointA pointB =
(
	i = addNewSpline ss
	addKnot ss i #corner #line PointA
	addKnot ss i #corner #line PointB
)
fn endLine = (
	updateShape ss
)
fn createPoly stk = (
	pol = SplineShape wirecolor:[0,255,0]
	addNewSpline pol
	sz = stk.size()
	if (sz>2) then (
		frst = stk.pop()
		addKnot pol 1 #smooth #curve frst
		for i = 2 to sz do (
			addKnot pol 1 #smooth #curve (stk.pop())
		)
		addKnot pol 1 #smooth #curve frst -- just to make sure the spline is closed (for cap modifier)
		close pol 1
		updateShape pol
		mod = Extrude amount:0.001 output:0
		addModifier pol (mod)
		addModifier pol (cap_holes ())
	)
)
-- evaluate expression and draw plant
fn visualize exp ang = (
	posStk = bstack ()
	headStk = bstack ()
	polyStk = bstack ()
	lenStk = bstack ()
	
	-- to be UIed
	pStart = [0,0,0]
	pLen = 10
	delta = ang
	--
	
	inPoly = 0 -- lets the prog know if we r drawing a polygon
	curPos = copy pStart
	curXAng = 0 -- yaw
	curYAng = 0 -- pitch
	curZAng = 0 -- roll
	xch = 0 -- angle change flags
	ych = 0
	zch = 0
	heading = matrix3 1
	startLine curPos
	
	for c = 1 to exp.count do (
		if (exp[c] == "+") then (
			curXAng += delta
		) else if (exp[c] == "-") then (
			curXAng -= delta
		) else if (exp[c] == "&") then (
			curYAng += delta
		) else if (exp[c] == "^") then (
			curYAng -= delta
		) else if (exp[c] == "\\") then (
			curZAng += delta
		) else if (exp[c] == "/") then (
			curZAng -= delta
		) else if (exp[c] == "|") then (
			curXAng += 180
		) else if (exp[c] == "[") then (
			posStk.push (copy curPos)
			headStk.push (copy curXAng)
			headStk.push (copy curYAng)
			headStk.push (copy curZAng)
		) else if (exp[c] == "]") then (
			curPos = posStk.pop ()
			curZAng = headStk.pop()
			curYAng = headStk.pop()
			curXAng = headStk.pop()
		) else if (exp[c] == "F") then (
			identity heading
			translate heading [0,0,pLen]
			rotatex heading curXAng
			rotatey heading curYAng
			rotatez heading curZAng
			translate heading curPos
			nextPos = pstart * heading
			addLine curPos nextPos
			curPos = nextPos
		) else if (exp[c] == "f") then (
			identity heading
			translate heading [0,0,pLen]
			rotatex heading curXAng
			rotatey heading curYAng
			rotatez heading curZAng
			translate heading curPos
			nextPos = pstart * heading
			curPos = nextPos
			if (inPoly==1) then (
				polyStk.push curPos
			)
		) else if (exp[c] == "{") then (
			inPoly = 1
			lenStk.push (copy pLen)
			pLen = 7 -- make the leaf size smaller
		) else if (exp[c] == "}") then (
			inPoly = 0
			createPoly polyStk
			polyStk.empty ()
			pLen = lenStk.pop ()
		)
	)
	
	endLine()
)

-- provide expression and call visualize
fn buildTree = (
	-- expressions that produce good results (abop.pdf pg:25)
	-- F[+F]F[-F]F (25.7)
	-- F[+F]F[-F][F] (20)
	-- FF-[-F+F+F]+[+F-F-F] (22.5)
	
	-- only use 'F' variable in the expression
	--expr = buildExp "F" (bMap #(keyvalue "F" "F[+F]F[-F]F")) 5 
	--e = buildExp "X" (bMap #(keyvalue "X" "F[+X]F[-X]+X", keyvalue "F" "FF")) 7
	--e = buildExp "X" (bMap #(keyvalue "X" "F[+X][-X]FX", keyvalue "F" "FF")) 7
	--e = buildExp "X" (bMap #(keyvalue "X" "F-[[X]+X]+F[+FX]-X", keyvalue "F" "FF")) 6
	--3D
	expr = buildExp "A" (bMap #(keyvalue "A" "///BY///BYA", keyvalue "B" "[&Y[+L][-L]YBF]", keyvalue "L" "FF'{+X-X-X+|+X-X}", keyvalue "X" "fX", keyvalue "Y" "FY")) 9
	visualize expr 22.5
)

-- test
 buildTree()

   

#24

Was able to generate the following plant with the expression:
expr = buildExp “A” (bMap #(keyvalue “A” “///BY///BYA”, keyvalue “B” “[&Y[+L][-L]YBF]”, keyvalue “L” “FF’{+X-X-X+|+X-X}”, keyvalue “X” “fX”, keyvalue “Y” “FY”)) 9
visualize expr 22.5

looked it up thru’: http://www.geocities.com/gplatl/LSystem/LSystem.html

-Bhupendra


#25

This thread has been automatically closed as it remained inactive for 12 months. If you wish to continue the discussion, please create a new thread in the appropriate forum.