! 2D "Map-L system" plant tissue development model ! Pretty much after deBoer, Fracchia, and Prusinkiewicz ! in the Alife II proceedings. ! Started 15 Sept. 1998 ! History of # of lines ! Sept 15 (reconstruction) 113 lines ! Sept 16, initialized rules & seed & drew 1 triangle: 254 lines. ! Sept 17, converted to "xy"s and "bounds": 245 lines. ! copyToPrev & started transformWall[s]: 300 lines. ! Sept 18, converted to half-walls: 348 lines. ! Sept 19, finished transformwall: 399 lines. ! sept 20, did some fiddling: 407 lines ! Sept 21, separated wallInsertWall()... ! It transforms! (without mating starts): 447 lines. ! Sept 22, started marryStart: 521 lines. ! Sept 23, More work on marryStart & co.: 548 lines. ! Sept 24, got freeHalfWall done: 572 lines. ! Put in new rules & cleaned superficial errors: 613 lines. ! Sept 25, put in debug lines & got it going: 640 lines. ! Commented 'em out and increased array bounds: Cells divide! 641 lines. ! ------------------------------------------------------------------------------- ! Sept 26, fiddling: 661 lines. ! Sept 27, It does physics! (albeit w/ exploding ! hyperwarts & unexplained asymmetries): 826 lines. ! ------------------------------------------------------------------------------- ! Sept 29, Added cells: 857 lines. ! Oct 2, Fixed a pressure-calculation bug that caused hyperwarts: 872 lines. ! Oct 4, Reverted to calculating pressure once per wall-- ! makes some sort of bug disappear: 877 lines. ! Oct 5, Colored the cells according to # of sides-- ! shows a problem with some cells: 936 lines ! Oct 6, Fixed how it assigns walls & cells to each other ! ( wallMate(wallNext(w)) vs. wallNext(wallMate(w)) ) ! Now the stored cell pressures work & it's faster. ! Fixed incomplete freeHalfWall() (not used yet). ! Lightened colors & increased DIMs to allow 2048 cells: 968 lines. ! Dec 27, Fixed, zoomed-out magnification while growing: 976 lines. ! ------------------------------------------------------------------------------- ! Wall stuff ! Actually each element in these "wall" arrays pertains to *half* a wall, either ! the front-right half or the back-left half. But by having an index to one ! half you have a handle on the whole wall. And the index to the other half (its "mate") ! serves as a handle to the other side of the wall going the other direction. ! (What's clockwise to the cell on one side of a wall is counterclockwise to ! the cell on the other side. The "next wall" pointers go clockwise.) ! ! Convention: you hold on to the index of the half of the wall with the ! "next" pointer that's going the way you're tracing around a cell or ! inserting new sub-walls--the front-right half from the point of ! view you're using. (But the wallNext pointer, whether of the front of a wall ! or of a start, points to the *back* half of the next wall, so you use the ! wallMate pointer of that to get its "front".) ! ! Wall-halves without mates represent "wall starts". ! let maxNWalls = 12301 dim wallType( 12301 ) ! = which rule applies to it. dim wallDirection( 12301 ) ! +1 if this is the head, -1 if this is the tail. ! 0 if this is the start of a symmetrical wall. dim wallIsOutside( 12301 ) ! 1 if this is part of the outside edge of the whole "plant" dim wallMate( 12301 ) ! the other half ( if any ) dim wallCorner( 12301 ) ! the corner at this end dim wallPrevCorner( 12301 ) ! in prev state of drawing dim wallNext( 12301 ) ! back half of neighbor wall ahead (from this p.o.v.) dim wallCell( 12301 ) ! Stuff for scanning through the collection of walls (their heads anyway) sub firstWall( i ) let i = 0 let lastNextWall = nWalls ! Save so that more walls can be added while scanning. call nextWall( i ) end sub sub nextWall( i ) do let i = i + 1 if i > lastNextWall then let i = 0 exit sub end if loop while wallDirection( i ) <> 1 ! Pick up only heads. end sub ! Corner stuff let maxNCorners = 8202 dim cornerXY( 8202, 2 ) dim cornerHV( 8202, 2 ) dim cornerForce( 8202, 2 ) dim cornerPrevForce( 8202, 2 ) dim cornerPrevXY( 8202, 2 ) ! in prev drawing state ! Cell stuff let maxNCells = 2105 dim cellWall1( 2105 ) dim cellPressure( 2105 ) ! Rule stuff let maxnrules = 103 ! one rule for each wall type dim ruleNParts( 103) ! number of sub-walls this wall type splits into ! A "part" is a sub-"wall" and an optional "start" between that subwall and ! the next. The last part isn't supposed to have a start. let maxRuleNParts = 8 dim ruleWallType( 103, 8 ) dim ruleWallDirection( 103, 8 ) ! 1 = same, -1 = reverse, never zero dim ruleStartSide( 103, 8 ) ! 1 = right, -1 = left, 0 = none dim ruleStartType( 103, 8 ) dim ruleStartDirection( 103, 8 ) ! 1 = away, -1 = toward me, 0 = symmetrical dim bounds(2,2), prevBounds(2,2) call setMacPallette call initRules call initSeed let doUndraw = 1 let dontUndraw = 0 let doDrag = 1 let dontDrag = 0 let doColorIt = 1 let dontColorIt = 0 let undraw = dontUndraw let r = 5 let g = 0 let b = 0 let maxCellCorners = 8 dim nSidesColor( 8 ) call initNSidesColor do call copyToPrev call doPhysics call getBounds( cornerXY(,), nCorners, bounds ) ! call printBounds( "bounds", bounds ) call adjustBounds( bounds, 1.1 ) call drawIt( undraw, doColorIt, bounds, prevBounds ) ! print ! line input prompt ":": a$ pause 4 ! Colored & physics should be mostly settled. let undraw = doUndraw ! let temp = r ! let r = g ! let g = b ! let b = temp ! call setrgb( r, g, b ) call transformWalls call getBounds( cornerXY(,), nCorners, bounds ) ! call printBounds( "bounds", bounds ) call adjustBounds( bounds, 1.1 ) call drawIt( undraw, dontColorIt, bounds, prevBounds ) pause 1 ! With cells just split but no color. loop sub drawIt( undraw, colorit, bounds(,), prevBounds(,) ) local bg, fg local wall ask background color bg ask color fg ! print set color fg call setBounds( bounds ) clear print "nCells ="; nCells if colorit = doColorIt then call paintCells call firstWall( wall ) do while wall <> 0 if undraw = doUndraw and wall <= prevNCorners and 1 = 0 then set color bg call setBounds( prevBounds ) call plotWall( wallPrevCorner, cornerPrevXY, wall ) set color fg call setBounds( bounds ) end if call plotWall( wallCorner, cornerXY, wall ) call nextWall( wall ) loop ! pause 1 end sub declare def rgbColor sub paintCells local cell, wall, corner, xy( 8, 2 ), i, saveFg for cell = 1 to nCells let wall = cellWall1( cell ) if wallIsOutside( wall ) = 0 then let i = 0 do while i < maxCellCorners if wallCell( wall ) <> cell then ! print "Cell"; cell; "--> walls"; cellWall1( cell ); "..."; wall; ! print "--> cell"; wallCell( wall ) ! line input prompt ":": a$ end if let i = i + 1 let corner = wallCorner( wall ) let xy( i, 1 ) = cornerXY( corner, 1 ) let xy( i, 2 ) = cornerXY( corner, 2 ) let wall = wallMate( wallNext( wall ) ) loop while wall <> cellWall1( cell ) mat redim xy( i, 2 ) ask color saveFg set color nSidesColor( i ) mat plot area: xy set color saveFg mat redim xy( maxCellCorners, 2 ) end if next cell end sub sub initNSidesColor let nSidesColor( 1 ) = rgbColor( 0, 0, 0 ) let nSidesColor( 2 ) = rgbColor( 0, 0, 0 ) let nSidesColor( 3 ) = rgbColor( 5, 1, 1 ) ! Red let nSidesColor( 4 ) = rgbColor( 5, 3, 2 ) ! Light Orange let nSidesColor( 5 ) = rgbColor( 5, 5, 0 ) ! Yellow let nSidesColor( 6 ) = rgbColor( 3, 5, 3 ) ! Light Green let nSidesColor( 7 ) = rgbColor( 3, 3, 5 ) ! Light Blue let nSidesColor( 8 ) = rgbColor( 5, 3, 5 ) ! Bright magenta? end sub sub plotWall( wc(), cxy(,), wall ) local c1, c2 let c1 = wc( wall ) let c2 = wc( wallMate( wall) ) plot lines: cxy( c1, 1 ), cxy( c1, 2 ); cxy( c2, 1 ), cxy( c2, 2 ) end sub sub getBounds( cxy(,), n, bounds(,) ) local i let bounds( 1, 1 ) = cxy( 1, 1 ) let bounds( 2, 1 ) = cxy( 1, 2 ) let bounds( 1, 2 ) = cxy( 1, 1 ) let bounds( 2, 2 ) = cxy( 1, 2 ) for i = 2 to n let bounds( 1, 1 ) = min( bounds( 1, 1 ), cxy( i, 1 ) ) let bounds( 2, 1 ) = max( bounds( 2, 1 ), cxy( i, 1 ) ) let bounds( 1, 2 ) = min( bounds( 1, 2 ), cxy( i, 2 ) ) let bounds( 2, 2 ) = max( bounds( 2, 2 ), cxy( i, 2 ) ) next i end sub sub adjustBounds( bounds(,), bigness ) ! Adjust to 4w x 3h and add a margin. local centerX, centerY, w, h let w = ( bounds( 2, 1 ) - bounds( 1, 1 ) ) * bigness let h = ( bounds( 2, 2 ) - bounds( 1, 2 ) ) * bigness let centerY = ( bounds( 1, 2 ) + bounds( 2, 2 ) ) / 2 let centerX = ( bounds( 1, 1 ) + bounds( 2, 1 ) ) / 2 if w / h > 4 / 3 then let h = w * 3 / 4 else let w = h * 4 / 3 let bounds( 1, 1 ) = centerX - w / 2 let bounds( 2, 1 ) = centerX + w / 2 let bounds( 1, 2 ) = centerY - h / 2 let bounds( 2, 2 ) = centerY + h / 2 end sub sub printBounds( name$, bounds(,) ) print name$; ":"; bounds( 1, 1 ); ","; bounds( 1, 2 ); "; "; print bounds( 2, 1 ); ","; bounds( 2, 2 ) end sub sub setBounds( bounds(,) ) set window bounds(1,1), bounds(2,1), bounds(1,2), bounds(2,2) end sub sub setrgb( r, g, b ) ! Uses the 6x6x6 section of the standard Mac pallette. if r<=0 and g<=0 and b<=0 then set color 255 else set color 36 * (5-r) + 6 * (5-g) + (5-b) end if end sub def rgbColor( r, g, b ) ! Uses the 6x6x6 section of the standard Mac pallette. if r<=0 and g<=0 and b<=0 then let rgbColor = 255 else let rgbColor = 36 * (5-r) + 6 * (5-g) + (5-b) end if end def sub copyToPrev local i, j mat wallPrevCorner = wallCorner let prevNWalls = nWalls mat cornerPrevXY = cornerXY let prevNCorners = nCorners mat prevBounds = bounds end sub sub transformWalls local wall call firstWall( wall ) do while wall <> 0 call transformWall( wall ) call nextWall( wall ) loop call firstAllStart( wall ) do while wall <> 0 call marryStart( wall ) call nextAllStart( wall ) loop end sub sub doPhysics local i, n let timeStep = .25 let dragConstant = .25 call clearPhysics call calcForces call calcAccel( dontDrag ) let n = int( sqr( nWalls ) ) call getBounds( cornerXY(,), nCorners, bounds ) ! call printBounds( "bounds", bounds ) call adjustBounds( bounds, 2.2 ) for i = 1 to n call drawIt( doUndraw, dontColorIt, bounds, prevBounds ) print i; "/"; n call copyToPrev mat cornerPrevForce = cornerForce call calcForces call calcAccel( doDrag ) next i end sub sub transformWall( parentWall ) local wall, mate, rule, n, c0, cn, x0, y0, xn, yn, i local prevWall, c let wall = parentWall ! otherwise we'd be changing the caller's "wall" let rule = wallType( wall ) let n = ruleNParts( rule ) let c0 = wallCorner( wallMate( wall ) ) let cn = wallCorner( wall ) let x0 = cornerXY( c0, 1 ) let y0 = cornerXY( c0, 2 ) let xn = cornerXY( cn, 1 ) let yn = cornerXY( cn, 2 ) ! The parent wall is replaced by the first subwall, ! then further subwalls (& 1 corner each) are inserted. ! Starts are created for subwalls 2..n, ! between the previous subwall and the current one, with the previous rule. call wallSetTypeDirection( wall, ruleWallType( rule, 1 ), ruleWallDirection( rule, 1 ) ) ! print wall; "==>"; wall; for i = 2 to n let prevWall = wall call newCorner( c ) let cornerXY( c, 1 ) = x0 + ( i - 1 ) * ( xn - x0 ) / n let cornerXY( c, 2 ) = y0 + ( i - 1 ) * ( yn - y0 ) / n call wallInsertWall( prevWall, c, wall, ruleWallType( rule, i ), ruleWallDirection( rule, i ) ) if ruleStartSide( rule, i - 1 ) = -1 then ! print ", /"; let mate = wallMate( wall ) call insertStart( mate, ruleStartType( rule, i - 1 ), ruleStartDirection( rule, i - 1 ) ) end if if ruleStartSide( rule, i - 1 ) = 1 then ! print ", \"; call insertStart( prevWall, ruleStartType( rule, i-1 ), ruleStartDirection( rule, i-1 ) ) end if ! print ","; wall; next i ! print end sub declare def newCell sub marryStart( firstStart ) local otherStart, rightType, rightDirection, next ! print "Mate for"; firstStart; "?" let rightType = wallType( firstStart ) let rightDirection = -wallDirection( firstStart ) let otherStart = firstStart do call nextCellStart( otherStart, firstStart ) ! print " try"; otherStart if otherStart = firstStart then ! We're around to the beginning again. call removeStartGetNext( firstStart ) ! Give up on this particular start but not others. exit sub ! marryStart will be called again to try any others. end if if wallType( otherStart ) = rightType and wallDirection( otherStart ) = rightDirection then exit do else ! If this one isn't going to work, identical ones won't either. ! & if it does work they won't get a chance. if wallType( otherStart ) = wallType( firstStart ) then call removeStartGetNext( otherStart ) end if end if loop ! Now otherStart is a good mate for firstStart. let wallMate( firstStart ) = otherStart let wallMate( otherStart ) = firstStart if wallDirection( firstStart ) = 0 then ! If it's symmetrical, give it direction. let wallDirection( firstStart ) = 1 let wallDirection( otherStart ) = -1 end if call wallClearCellStarts( firstStart ) ! Clear any other starts call wallClearCellStarts( otherStart ) ! on both sides of newly divided cell. let cellWall1( wallCell( firstStart ) ) = firstStart ! Make sure old cell's wall1 is in the cell. ! print "cell"; wallCell( firstStart ); "-->"; firstStart let cell = newCell( otherStart ) end sub sub wallSetTypeDirection( wall, type, direction ) local mate let mate = wallMate( wall ) let wallType( wall ) = type let wallType( mate ) = type let wallDirection( wall ) = direction let wallDirection( mate ) = -direction end sub sub wallInsertWall( wall, corner, wall2, type, direction ) local mate2, next, last call newWall( wall2 ) let mate2 = wallMate( wall2 ) let wallIsOutside( wall2 ) = wallIsOutside( wall ) let wallIsOutside( mate2 ) = wallIsOutside( wallMate( wall ) ) let wallCell( wall2 ) = wallCell( wall ) let wallCell( mate2 ) = wallCell( wallMate( wall ) ) let wallCorner( wall2 ) = wallCorner( wall ) let wallCorner( wall ) = corner let wallCorner( mate2 ) = corner let next = wallNext( wall ) let last = next do while wallNext( last ) <> wall ! Scoot around to the one that points back. ! print "<"; last; ">"; let last = wallNext( last ) loop let wallNext( wall2 ) = next let wallNext( last ) = wall2 let wallNext( mate2 ) = wall let wallNext( wall ) = mate2 ! print "("; wall; "<->"; mate2; ")"; if last = next then ! print "("; next; "<->"; wall2; ")"; else ! print "("; last; "->"; wall2; "->"; next; ")"; end if call wallSetTypeDirection( wall2, type, direction ) end sub sub insertStart( prev, type, direction ) local start call newHalfWall( start ) let wallCell( start ) = wallCell( prev ) ! print "["; prev; "]"; let wallNext( start ) = wallNext( prev ) let wallNext( prev ) = start ! print "("; prev; "->"; start; "->"; wallNext( start ); ")"; let wallCorner( start ) = wallCorner( prev ) let wallType( start ) = type let wallDirection( start ) = -direction let wallMate( start ) = 0 let wallIsOutside( start ) = 0 ! print start; end sub ! Scan for starts out of the whole array. ! Each one represents a cell with at least one start! sub firstAllStart( i ) let i = 1 call nextAllStart( i ) end sub sub nextAllStart( i ) do ! Recheck the same index again. if wallMate( i ) = 0 then exit sub let i = i + 1 loop while i <= nWalls ! Pay attention to nWalls as it will be shrinking. let i = 0 end sub ! Next start around this cell, until we hit the original start or wall. sub nextCellStart( wall, first ) local mate do let wall = wallNext( wall ) let mate = wallMate( wall ) ! print mate; "?"; ! pause 1 if mate = 0 then exit sub ! If it's a start, return with it. let wall = mate ! If it's a wall, get the front. loop while wall <> first end sub ! Remove a start and give back a pointer to the following wall. ! (Can't deal with another start attached to the same point.) sub removeStartGetNext( start ) local prev, next ! print "removeStartGetNext("; start; ")..." let next = wallNext( start ) let prev = wallNext( next ) let wallNext( prev ) = next call freeHalfWall( start ) let start = wallMate( next ) end sub def wallPointingBack( wall ) ! Who at the next corner points back to wall? local next let next = wallNext( wall ) do while wallNext( next ) <> wall ! Scoot around this corner. let next = wallNext( next ) loop let wallPointingBack = next end def ! "Free" a half-wall by moving the last wall into its space and decrementing nWalls. ! The half-wall at nWalls moves into wall's place. ! nWalls' mate & neighbors stay put. sub freeHalfWall( wall ) local mate let mate = wallMate( nWalls ) let wallType( wall ) = wallType( nWalls ) let wallDirection( wall ) = wallDirection( nWalls ) let wallCell( wall ) = wallCell( nWalls ) let wallIsOutside( wall ) = wallIsOutside( nWalls ) let wallCorner( wall ) = wallCorner( nWalls ) let wallMate( wall ) = mate let wallNext( wall ) = wallNext( nWalls ) let wallNext( wallPointingBack( nWalls ) ) = wall if mate <> 0 then let wallMate( mate ) = wall let nWalls = nWalls - 1 end sub ! Remove all starts on the right as you go around clockwise. sub wallClearCellStarts( firstWall ) local wall let wall = firstWall ! print "wallClearCellStarts("; firstWall; ")..." do call nextCellStart( wall, firstWall ) ! print "hmm"; wall if wall = firstWall then exit do call removeStartGetNext( wall ) loop ! Was that first wall a start, too? if wallMate( wall ) = 0 then call removeStartGetNext( wall ) end sub sub clearPhysics local i mat cornerHV = zer ! for i = 1 to nCorners ! next i end sub declare def cellArea, wallCellArea, triangleArea sub calcForces local wall, c for c = 1 to nCells let cellPressure( c ) = 1 / cellArea( c ) next c ! pause 5 mat cornerForce = zer call firstWall( wall ) do while wall <> 0 call wallCalcForces( wall ) call nextWall( wall ) loop end sub sub calcAccel( whetherToDrag ) local c, xOrY, f, a, v for c = 1 to nCorners for xOrY = 1 to 2 ! X and Y can be done independently. let f = cornerForce( c, xOrY ) if whetherToDrag = doDrag then ! Drag is proportional to change in the other forces. ! Those forces are related to local distances, ! so change in them is related to local velocities. let a = f + dragConstant * ( f - cornerPrevForce( c, xOrY ) ) / prevTimeStep else let a = f end if let v = cornerHV( c, xOrY ) let cornerXY( c, xOrY ) = cornerXY( c, xOrY ) + ( v + a * timeStep * .5 ) * timeStep let cornerHV( c, xOrY ) = v + a * timeStep next xOrY next c let prevTimeStep = timeStep end sub sub wallCalcForces( wall ) local dx, dy, len call wallDxDy( wall, dx, dy ) ! Add in the spring force: ! print "wall"; wall; "tension:"; dx; dy, call cornerVectorAdd( cornerForce, wallCorner( wallMate( wall ) ), dx, dy ) call cornerVectorAdd( cornerForce, wallCorner( wall ), -dx, -dy ) ! And the pressures from the cells on both sides ! (proportional to wall vector rotated 90 degrees): call wallCalcPressure( wall, -dy, dx ) call wallCalcPressure( wallMate( wall ), dy, -dx ) end sub sub wallCalcPressure( wall, dx, dy ) local pressure, px, py if wallIsOutside( wall ) = 1 then ! print "no wall"; wall; "pressure", exit sub end if let pressure = cellPressure( wallCell( wall ) ) ! let pressure = 1 / wallCellArea( wall ) let px = dx * pressure let py = dy * pressure ! print "wall"; wall; "pressure:"; px; py, call cornerVectorAdd( cornerForce, wallCorner( wallMate( wall ) ), px, py ) call cornerVectorAdd( cornerForce, wallCorner( wall ), px, py ) end sub def cellArea( cell ) = wallCellArea( cellWall1( cell ) ) def wallCellArea( wall1 ) ! Doesn't work if there are any starts. local wall, area, c1, c2, c3 let area = 0 let c1 = wallCorner( wall1 ) let wall = wallMate( wallNext( wall1 ) ) let c2 = wallCorner( wall ) let wall = wallMate( wallNext( wall ) ) do let c3 = wallCorner( wall ) let area = area + triangleArea( c1, c2, c3 ) let c2 = c3 ! c1 stays the same. let wall = wallMate( wallNext( wall ) ) loop while wall <> wall1 let wallCellArea = area ! print "cell"; cell; "area"; area, end def def triangleArea( c1, c2, c3 ) ! Area is positive if c1, c2, c3 are clockwise, else negative. local x21, y21, x31, y31, a let x21 = cornerXY( c1, 1 ) - cornerXY( c2, 1 ) let y21 = cornerXY( c2, 2 ) - cornerXY( c1, 2 ) let x31 = cornerXY( c1, 1 ) - cornerXY( c3, 1 ) let y31 = cornerXY( c3, 2 ) - cornerXY( c1, 2 ) let a = ( x21 * y31 - x31 * y21 ) * .5 let triangleArea = a ! print "triangle", c1; c2; c3; "area"; a, end def sub cornerVectorAdd( array(,), i, dx, dy ) let array( i, 1 ) = array( i, 1 ) + dx let array( i, 2 ) = array( i, 2 ) + dy end sub sub wallDxDy( wall, dx, dy ) local c0, c1 let c0 = wallCorner( wallMate( wall ) ) let c1 = wallCorner( wall ) let dx = cornerXY( c1, 1 ) - cornerXY( c0, 1 ) let dy = cornerXY( c1, 2 ) - cornerXY( c0, 2 ) end sub sub newWall( wall ) local mate call newHalfWall( mate ) call newHalfWall( wall ) let wallMate( wall ) = mate let wallMate( mate ) = wall end sub sub newHalfWall( wall ) let nWalls = nWalls + 1 if nWalls > maxNWalls then print "Too many walls ( >"; maxNWalls; ")" stop end if let wall = nWalls let wallIsOutside( wall ) = 0 let wallCell( wall ) = 0 end sub def newCell( wall1 ) local wall let nCells = nCells + 1 if nCells > maxNCells then print "Too many cells ( >"; nCells; ")" stop end if let cellWall1( nCells ) = wall1 let wall = wall1 ! print "Cell"; nCells; ":"; do ! print wall; let wallCell( wall ) = nCells let wall = wallMate( wallNext( wall ) ) loop while wall <> wall1 ! print ! line input prompt ":": a$ let newCell = nCells end def sub newCorner( corner ) let nCorners = nCorners + 1 if nCorners > maxNCorners then print "Too many corners ( >"; maxNCorners; ")" stop end if let corner = nCorners end sub sub initRules local rule, part, n let nRules = 0 do read rule if rule = 0 then exit do let nRules = nRules + 1 if rule <> nRules then print "Rule in position"; nRules; "says it's number"; rule stop end if if nRules > maxNRules then print "Too many rules ( >"; maxNRules; ")" stop end if let ruleNParts( rule ) = 0 do read n if n = 0 then exit do if ruleNParts( rule ) >= maxRuleNParts then print "Too many parts in rule"; rule; "( >"; maxRuleNParts; ")" stop end if let ruleNParts( rule ) = ruleNParts( rule ) + 1 let part = ruleNParts( rule ) let ruleWallType( rule, part ) = n read ruleWallDirection( rule, part ) read ruleStartSide( rule, part ) if ruleStartSide( rule, part ) <> 0 then read ruleStartType( rule, part ), ruleStartDirection( rule, part ) end if loop loop end sub sub initSeed local wall, mate, label, corner, type, outsideWall, wall1, cell ! Read the number of one half-wall that is on the outside of the whole plant. read outsideWall ! Read the graph structure of the walls. let nWalls = 0 do read label if label = 0 then exit do call newWall( wall ) if wall <> label then print "Seed wall in position"; wall; "says it's number"; label stop end if read type call wallSetTypeDirection( wall, type, 1 ) let mate = wallMate( wall ) read wallCorner( mate ), wallCorner( wall ) read wallNext( mate ), wallNext( wall ) loop ! Read the positions of the corners let nCorners = 0 do read label if label = 0 then exit do call newCorner( corner ) if corner <> label then print "Seed corner in position"; corner; "says it's number"; label stop end if read cornerXY( corner, 1 ), cornerXY( corner, 2 ) loop ! Mark all the outside walls. let wall = outsideWall ! print "Outside:"; do ! print wall; let wallIsOutside( wall ) = 1 let wall = wallMate( wallNext( wall ) ) loop while wall <> outsideWall ! print ! line input a$ ! Create cells within all those walls... let nCells = 0 for wall = 1 to nWalls if wallIsOutside( wall ) = 0 and wallCell( wall ) = 0 then let cell = newCell( wall ) next wall end sub sub setMacPallette for r = 0 to 5 for g = 0 to 5 for b = 0 to 5 if r=0 and g=0 and b=0 then set color mix(255) r/5, g/5, b/5 else set color mix(36*(5-r)+6*(5-g)+(5-b)) r/5, g/5, b/5 end if next b next g next r end sub ! Rules ! rules :== [rule,]... 0 ! rule :== rulenumber, [part,]... 0 ! part :== wallType, wallDirection, start ! start :== { startSide, startType, startDirection } | 0 data 1 ! 1> ==> 2> data 2, 1, 0 data 0 data 2 ! 2> ==> 1>, \3, 1> data 1, 1, 1, 3, 0 data 1, 1, 0 data 0 data 3 ! 3> ==> 5>, /3, 4>, \3, 5> data 5, 1, -1, 3, 0 data 4, 1, 1, 3, 0 data 5, 1, 0 data 0 data 4 ! 4> ==> 4> data 4, 1, 0 data 0 data 5 ! 5> ==> 3> data 3, 1, 0 data 0 data 0 ! Seed -- the initial walls and corners ! seed :== walls, corners ! walls :== [wall,]... 0 ! wall :== wallnumber, type, backCorner, frontCorner, backLeftWall, frontRightWall ! Wall numbers must be even! ! corners :== [ cornernumber, x, y, ]... 0 ! walls data 1 ! Wall # 1 is an outside wall. data 2, 1, 1, 2, 8, 3 data 4, 2, 2, 3, 2, 5 data 6, 1, 3, 4, 4, 7 data 8, 2, 4, 1, 6, 1 data 0 ! corners data 1, -.5, .5 data 2, .5, .5 data 3, .5, -.5 data 4, -.5, -.5 data 0 ! ! Original not-changing-much triangle. The seed walls aren't linked correctly here. ! rules data 1 ! 1> ==> 2>, 3> data 2, 1, 0 data 3, 1, 0 data 0 data 2 ! 2> ==> 2> data 2, 1, 0 data 0 data 3 ! 3> ==> 3> data 3, 1, 0 data 0 data 0 ! walls data 1 ! Wall # 1 is an outside wall. data 2, 1, 1, 2, 0, 4 data 4, 2, 2, 3, 0, 6 data 6, 3, 3, 1, 0, 2 data 0 ! corners data 1, 0, 1.5 data 2, -2, -1 data 3, 2, -1 data 0 end