 # 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.