# # cod3d.icn -- a code analyzer tool # $ifdef _UNIX $define PS "/" $define CVE_DAT "/home/jeffery/cve/dat" $define COPY "cp" $else $define PS "\\" $define CVE_DAT "\\jeffery\\cve\\dat" $define COPY "copy" $endif link basename global totalcount, authors, languages, directories, total_loc, city, # root of street tree curstreet, # current node fjson, # json file. for the mapping. not yet JSON. show2D # display the 2D map procedure main(argv) if argv[1]=="-2D" then { show2D := 1; pop(argv) } else if getenv("DISPLAY") then { show2D := 1 } /WIDTH := 1024 /HEIGHT := WIDTH totalcount := 0 authors := table(0) languages := table(0) directories := table() write("Welcome to COD3D") # # If there are 0 or 1 arguments, the root is . or the named argument. # if there are multiple arguments, the root should be above them. # if *argv>1 then city := street("") if *argv=0 then { analyze(".") } else { origdir := chdir() every s := !argv do { nam := basename(s) write("\t", nam) newstreet := street(nam) if not (/city := newstreet) then { curstreet.add(newstreet) } curstreet := newstreet chdir(s) analyze_dir(".") chdir(origdir) curstreet := curstreet.parent } } authors["Ralph E. Griswold"] +:= authors["Ralph Griswold"] delete(authors, "Ralph Griswold") authors["Ralph E. Griswold"] +:= authors["Ralph E. Griswold & Don Ward"] authors["Don Ward"] +:= authors["Ralph E. Griswold & Don Ward"] delete(authors, "Ralph E. Griswold & Don Ward") authors["Gregg M. Townsend"] +:= authors["Gregg Townsend"] delete(authors, "Gregg Townsend") authors["Robert J. Alexander"] +:= authors["Bob Alexander"] delete(authors, "Bob Alexander") authors["Stephen B. Wampler"] +:= authors["Stephen W. Wampler"] + authors["Stephen Wampler"] delete(authors, "Stephen W. Wampler") delete(authors, "Stephen Wampler") authors["Clinton L. Jeffery"] +:= authors["Clinton Jeffery"]+authors["Clint Jeffery"]+ authors["C. Jeffery"]+authors["Shea Newton and Clinton Jeffery"] authors["Shea Newton"] +:= authors["Shea Newton and Clinton Jeffery"] delete(authors, "Clinton Jeffery") delete(authors, "Clint Jeffery") delete(authors, "C. Jeffery") delete(authors, "Shea Newton and Clinton Jeffery") authors["Phillip Lee Thomas"] +:= authors["Phillip Lee Thomas and"] delete(authors, "Phillip Lee Thomas and") authors["Hani bani Salameh"] +:= authors["Hani Bani-Salameh for CVE"] delete(authors, "Hani Bani-Salameh for CVE") authors["Ziad Al-Sharif"] +:= authors["zsharif@gmail.com"] delete(authors, "zsharif@gmail.com") authors["Nevin J. Liber"] +:= authors["Nevin Liber"] delete(authors, "Nevin Liber") authors["William S. Evans"] +:= authors["Will Evans"] delete(authors, "Will Evans") authors["David A. Gamey"] +:= authors["David Gamey"] delete(authors, "David Gamey") authors["Richard L. Goerwitz"] +:= authors["Richard Goerwitz"] delete(authors, "Richard Goerwitz") authors["Chris Tenaglia"] +:= authors["C. Tenaglia"] delete(authors, "C. Tenaglia") delete(authors, "5 Dec. 89") write("total: ", totalcount, " lines by ", *authors, " authors") longest_author_name := 0 every longest_author_name <:= *key(authors) write("longest author name ", longest_author_name) L := sort(authors, 4) i := 0 while k := pop(L) do { writes(" ", left( k || ": " || pop(L), 24)) i +:= 1 if i > 2 then { write(); i := 0 } } write(languages[".icn"], " lines of Icon/Unicon") write(languages[".c"]+languages[".h"], " lines of C") write(languages[".r"]+languages[".ri"], " lines of RTL(C)") write(*directories, " directories") L2 := sort(directories, 4) while k := pop(L2) do{ write("\t", k, ": ", total_loc := pop(L2)) } cod2d() end procedure analyze(fn) if fn === "" then fail if fn ? (="./conf" & tab(many(&digits)) & = ".dir") then fail if not (st := stat(fn)) then stop("COD: can't analyze ", image(fn)) else if st.mode[1] == "d" then { # write(" ", image(fn)) origdir := chdir() chdir(fn) newstreet := street(fn) if not (/city := newstreet) then { curstreet.add(newstreet) if newstreet.getlevel() = 2 then write("add ", image(newstreet.name), " to ", image(curstreet.name)) } curstreet := newstreet analyze_dir(".") chdir(origdir) if *newstreet.children > 0 then { # write("newstreet ", newstreet.name, " had ", *newstreet.children, " kids") numC := 0 every kid := !newstreet.children do if kid.lang === "C" then numC +:= 1 if numC = *newstreet.children then { newstreet.lang := "C" } } curstreet := curstreet.parent } else { # write("\t ", image(fn)) analyze_file(fn) } return end procedure extension(fn) static extensions_to_try initial extensions_to_try := set([".icn",".c",".h",".r",".ri"]) this_extension := "" every i := find(".", fn) if /i then fail this_extension := fn[i:0] if member(extensions_to_try, this_extension) then return this_extension end procedure analyze_file(fn) local count, x, fil # write("analyzing ", image(fn)) if not (x := extension(fn)) then { # write("ignoring ", image(fn)) } else { if x ~=== ".icn" then curstreet.lang := "C" if fil := file(fn) then { fil.analyze() # write(fn, "... ", *fil.contents, " lines") # was: # insert(curstreet.buildings, fil) curstreet.add(fil) return fil } } end procedure insert_author(s) if *s = 0 then fail s ? { if (fn := tab(many(&letters))) & =" " & tab(any(&ucase)) & =" " & (ln := tab(many(&letters))) then s := fn || " " || ln } authors[s] +:= 1 return end procedure analyze_dir(fn) local beforeme, f # write("analyzing directory ", image(fn)) beforeme := totalcount if f := open(fn || "/*") then { while newf := read(f) do { if (newf==("."|".."|".svn")) | (newf[-2:0] == ("/."|"\\.")) then next if match("./", newf) then { # drop ./ prefix newf := newf[3:0] } analyze(newf) } close(f) # write("directory ", image(fn), " had ", totalcount - beforeme, " lines") directories[fn] := (0 < (totalcount - beforeme)) } end class street(name, # dirname value,# size, derived from LOC lang, level, # distance from root buildings, # non-street data payload parent, children # other streets ) method draw() end method getvalue() # if \value then return value value := 0 # every value +:= (!buildings).loc() do { # } every kid := !children do { value +:= kid.getvalue() } return value end method getlevel() if \level then return level if /parent then level := 1 else level := 1 + parent.getlevel() return level end method add(kid) put(children, kid) kid.parent := self kid.getlevel() end initially /lang := "Unicon" children := [] buildings := set() end class building(name, typ, fil, line, extent) method num_methods() return 1 end method loc() return extent end end class classbuilding : building(methods) method num_methods() return *methods end initially(n,t,f,l,e,m) name := n; typ := t; fil := f; line := l; extent := e; methods := m /methods := [] end # # A file has a name and a list of strings (contents). It is also a # street that has a collection of buildings distributed around it. # class file : street(name,contents) method getvalue() return *contents end method analyze() local x := extension(name) #writes("analyzing ", name) if x == ".icn" then { #write(" as unicon") return analyze_unicon() } else if x == (".c"|".h") then { #write("as C") return analyze_C() } else if x == (".r"|".ri") then { #write("as RTL") return analyze_RTL() } write("unrecognized") fail end # we really don't want to parse C for this quick and dirty prototype # upgrade this to a end-previous-when-you-start-next approach method analyze_C() local lineno, kidvalue lineno := 1 while lineno <= *contents do { line := contents[lineno] line ? { if (typ <- =("int"|"char"|"pointer"|"WINDOW"|"void")) & tab(many(' \t')) & (="*"|"") & (tab(many(' \t'))|"") & (ident <- tab(many(&letters++&digits++'_'))) & (tab(many(' \t'))|"") & ="(" then { # wow, we really don't want to parse C if line[-1] == ";" then { lineno +:= 1; next } startlineno := lineno kidvalue := 3 lineno +:= 2 insert(buildings, building(ident, typ, name, startlineno, kidvalue)) #write("Cinsert ", indent, " into ", name) } } lineno +:= 1 } end method analyze_RTL() local lineno, kidvalue lineno := 1 while lineno <= *contents do { line := contents[lineno] line ? { if (typ <- ="function") & ="{" & (((="0,"|"") & tab(many(&digits))) | ="*") & ="}" & tab(many(' \t')) & (ident <- tab(many(&letters++&digits++'_'))) then { startlineno := lineno kidvalue := 1 lineno +:= 1 while lineno <= *contents & contents[lineno] ~== "end" do { lineno +:= 1; kidvalue +:= 1 } insert(buildings, building(ident, typ, name, startlineno, kidvalue)) } else if (typ <- ="operator") & ="{" & (((="0,"|"") & tab(many(&digits))) | ="*") & ="}" & tab(many(' \t')) & tab(many('!?[:]')) & tab(many(' \t')) & (ident <- tab(many(&letters++&digits++'_'))) then { startlineno := lineno kidvalue := 1 lineno +:= 1 while lineno <= *contents & contents[lineno] ~== "end" do { lineno +:= 1; kidvalue +:= 1 } insert(buildings, building(ident, typ, name, startlineno, kidvalue)) } } lineno +:= 1 } end method analyze_unicon() local lineno, kidvalue lineno := 1 while lineno <= *contents do { #write("w ", lineno, "/", *contents, " in ", image(name)) line := contents[lineno] line ? { if (typ <- ="procedure") & tab(many(' \t')) & (ident <- tab(many(&letters++&digits++'_'))) then { startlineno := lineno kidvalue := 1 lineno +:= 1 while lineno <= *contents & contents[lineno] ~== "end" do { lineno +:= 1; kidvalue +:= 1 } #write("adding ", ident, " to ", name) insert(buildings, building(ident, typ, name, startlineno, kidvalue)) } else if (typ <- ="class") & tab(many(' \t')) & (ident <- tab(many(&letters++&digits++'_'))) then { kidvalue := 1 lineno +:= 1 meths := [] while lineno <= *contents & contents[lineno] ~== "end" do { contents[lineno] ? { if (tab(many(' \t'))|"") & ="method" & tab(many(' \t')) & (methname <- tab(many(&letters++&digits++'_'))) then { # could build an object for each method here put(meths, methname) lineno +:= 1; kidvalue +:= 1 while lineno <= *contents & contents[lineno] ~== "end" do { lineno +:= 1; kidvalue +:= 1 } } } lineno +:= 1; kidvalue +:= 1 } #write("adding ", ident, " to ", name) insert(buildings, classbuilding(ident, typ, name, lineno, kidvalue, meths)) } else if ="#" & tab(many(' \t')) & ="Author:" & tab(many(' \t')) then{ while insert_author(trim(tab(find(",")), '\t ')) do { move(1) tab(many(' \t')) ="modified by " ="enhanced by " ="tweaks by " ="additions by " ="revised by " ="from Rosetta Code directory" ="inspired by kaleido.icn by " ="based on code by " ="and " } if insert_author(trim(tab(find("(")), ' \t')) then { lineno +:= 1 next } if insert_author(trim(tab(find(" and ")), ' \t')) then move(5) insert_author(trim(tab(0), ' \t')) } } lineno +:= 1 } count := *contents languages[x] +:= count totalcount +:= count end method loc() return *contents end initially local f self.street.initially() f := open(name) | stop("can't open ", image(name)) if not (contents := [: !f :]) then { if name ~=== "define.h" then write("empty? ", image(name)) contents := [] } close(f) end global pixel_unit global WIDTH, HEIGHT procedure cod2d() if \show2D then &window := open("cod2d", "g", "size="||WIDTH||","||HEIGHT) if not (pixel_unit := WIDTH / real(total_loc)) then stop("can't calculate pixel_unit") render2D(city) render3D(city) Event() end # # What all goes into generating a new model? # there is a model.dat file to write, and textures, texture_coords.info... # The contents to be written to the model.dat file need to be accumulated # in a data structure so that they can be written out in different # sections, e.g. an obstacles section, a decorations section etc. # # There is a question of how to map 2D pixels onto 3D world units. # Start with 10.0 meters per pixel, tweak from there. # $define METERS_PER_PIXEL 10.0 procedure render3D(node, x:0, y:0, w, h, parents_lw:0) local wd, ht olddir := chdir() if not chdir(CVE_DAT) then { stop("cannot chdir to ", image(CVE_DAT)) } if not stat(node.name) then { mkdir(node.name) write("created ", CVE_DAT, PS, node.name) } else write(node.name, " already exists in ", CVE_DAT) if not chdir(node.name) then { stop("chdir to ", CVE_DAT, PS, node.name," failed") } if not stat("textures") then { mkdir("textures") write("created ", CVE_DAT, PS, node.name, PS, "textures") } else write("textures already exists in ", CVE_DAT, PS, node.name) write("WriteImage again...") if WriteImage("textures" || PS || node.name || "_floor.gif") then { write("wrote ", "textures" || PS || node.name || "_floor.gif") } else stop("writeimage textures" || PS || node.name || "_floor.gif failed") system(COPY||" " || ".." || PS || "uidaho" || PS || "textures" || PS || "sky3.jpg" || " textures" || PS || "sky3.jpg") wd := WIDTH * METERS_PER_PIXEL ht := HEIGHT * METERS_PER_PIXEL # 2D "height" == z length tc_info := "textures" || PS || "texture_coord.info" remove(tc_info) chdir("textures") if f := open("texture_coord.info", "w") then { writes(f, "# texture_coord.info contains Width/Height in real world\n", "# coordinates for each texture image\n", "sky3.jpg\n{\n", " name sky\n", " real_world_x ", 256.0, "\n", " real_world_y ", 32.0, "\n", "}\n", node.name, "_floor.gif\n{\n", " name sky\n", " real_world_x ", wd, "\n", " real_world_y ", ht, "\n", "}\n") close(f) } else write("can't write ",image(tc_info)," from ", chdir()|"chdir failed") chdir("..") remove("model.dat") fjson := open("model.json","w") if f := open("model.dat", "w") then { write("set world size to ", wd, "x", ht, " meters") writes(f, "# City template model\n\n", "default {\n", "name ", node.name, "\n", "home [",wd/2, ",", 0.0, ",",ht/2, "]\n", "angle 4.6\n", "origin_node ", node.name, "\n", "}\n\n", "Room {\n", "name ", node.name, "\n", "x 0\n", "y 0\n", "z 0\n", "w ", wd, "\n", "h ", ht, "\n", "l ", ht, "\n", "texture sky3.jpg\n", "floor Wall { texture ", node.name, "_floor.gif }\n", "ceiling Wall { texture sky3.jpg }\n", "obstacles [\n") render3Da(f, node, x, y, w, h, parents_lw) writes(f,"]\n", # "decorations [\n", # "]\n", "}\n") close(f) } else write("can't write model.dat from ", chdir()) close(fjson) chdir(olddir) write("render3D done, wrote model.dat") end # render a building at (x,y,z,l,h,w) # kid object decides what sizes (l,h,w) to use. # as an extra wrinkle, it depends on if your street is horizontal or vertical # l (length in x) = # h (height) = 4.0 meters per method # w (width in z) = procedure render3DBuilding(f, kid, x,y,z,is_vertical) l := 2.0 * METERS_PER_PIXEL h := 4.0 * kid.num_methods() w := 2.0 * METERS_PER_PIXEL kidimage := image(kid.name) write(fjson, kidimage, " : { \"source\" : [", image(kid.fil),",",kid.line,",",kid.extent, "] ,\n",repl(" ",*kidimage)," \"world\": [",x,",",y,",",z,", ",l,",",h,",",w,"]},") write(f, "Box {\n", " Wall {\n", " texture newside.gif\n", " coords [ ", r(x),",", r(y),",", r(z),", ", r(x),",", r(y+h),",", r(z),", ", r(x),",", r(y+h),",", r(z+w),", ", r(x),",", r(y),",", r(z+w)," ]\n", " }\n", " Wall {\n", " texture newside.gif\n", " coords [ ", r(x+l),",", r(y),",", r(z),", ", r(x+l),",", r(y+h),",", r(z),", ", r(x+l),",", r(y+h),",", r(z+w),", ", r(x+l),",", r(y),",", r(z+w)," ]\n", " }\n", " Wall {\n", " texture newside.gif\n", " coords [ ",r(x),",", r(y),",", r(z),", ", r(x),",", r(y+h),",", r(z),", ", r(x+l),",", r(y+h),",", r(z),", ", r(x+l),",", r(y),",", r(z)," ]\n", " }\n", " Wall {\n", " texture newside.gif\n", " coords [ ",r(x),",", r(y),",", r(z+w),", ", r(x),",", r(y+h),",", r(z+w),", ", r(x+l),",", r(y+h),",", r(z+w),", ", r(x+l),",", r(y),",", r(z+w)," ]\n", " }\n", " }\n") end procedure r(v) local s := string(v) s ?:= tab(find(".")) || move(3) return s end # # The real render3D function. # Originally we pass in a file, and write into it. # For maximum flexibility eventually we may want to accumulate it all in # some list structure instead, and write that out afterwards. # procedure render3Da(f, node, x:0, y:0, w, h, parents_lw:0) if node.name == "src" then write("src: ", node.getvalue()) if node.getvalue() = 0 then fail /w := WIDTH /h := HEIGHT if node.getvalue() === 0 then lw := 1 else lw := integer(log(node.getvalue(), &e)) - node.level*2 if lw < 1 then lw := 1 else lw +:= 1 mywidth := integer(pixel_unit * node.getvalue()) # if node.getvalue() < 100 then # write(node.name, " value ", node.getvalue(), # " kids ", *node.children, # " linewidth ", lw, " mywidth ", mywidth) $define SCALER 2.0 if node.level % 2 = 1 then { # horizontal mymax := WIDTH mywidth := integer(mywidth * SCALER + parents_lw) if mywidth > mymax then mywidth := mymax # Fg(if node.lang==="C" then "brown" else "black") xA := x+w/2-mywidth/2 xB := x+w/2+mywidth/2 # DrawLine(xA, y+h/2, xB, y+h/2) # if (node.level <= 3 | mywidth > 10) & lw > 1 then { # Fg("dark green") # DrawString(x+w/2-mywidth/2,y+h/2-lw/2-4, node.name) # Fg("black") # } # see if any kids need to be excluded k := 1 while k <= *node.children do { kid := node.children[k] if kid.getvalue() < 100 then { delete(node.children, k) next } if kid.name === ("doc"|"bin"|"plugins"|"asm") then { delete(node.children, k) next } if kid.name === ("lib") & node.name === "src" then { delete(node.children, k) next } k +:= 1 } # perform an n-kid sectioning every k := 1 to *node.children do { kid := node.children[k] x0 := xA + (xB-xA) * real(k-1) / *node.children x1 := xA + (xB-xA) * real(k) / *node.children render3Da(f, kid, x0, y, x1-x0, h, lw) } # layout buildings. if *node.buildings > 0 then { # Fg("pale blue") k := 1 every kid := !node.buildings do { x0 := xA + (xB-xA) * real(k-1) / *node.buildings x1 := xA + (xB-xA) * real(k) / *node.buildings if k%2=1 then { # odd on top wd := 2 # x1-x0-2; if wd<1 then wd:=1 ht := 2 # ... y0 := y+h/2-lw/2-ht-2 } else { # even on bottom wd := 2 # x1-x0-2; if wd<1 then wd:=1 ht := 2 # ... y0 := y+h/2 + lw/2 + 2 } render3DBuilding(f, kid, x0 * METERS_PER_PIXEL, 0.0, y0 * METERS_PER_PIXEL) # FillRectangle(x0, y0, wd, ht) k +:= 1 } } } else { # vertical if \&window then mymax := HEIGHT-WAttrib("fheight")*2 else mymax := HEIGHT- 28*2 mywidth := integer(mywidth * SCALER + parents_lw) if mywidth > mymax then mywidth := mymax # Fg(if node.lang==="C" then "brown" else "black") yA := y+h/2-mywidth/2 yB := y+h/2+mywidth/2 # DrawLine(x+w/2,yA, x+w/2,yB) # tw := TextWidth(node.name) # if node.level <= 3 | mywidth > 20 then { # Fg("dark green") # DrawString(x+w/2-tw/2,y+h/2+mywidth/2+15, node.name) # Fg("black") # } # see if any kids need to be excluded k := 1 while k <= *node.children do { kid := node.children[k] if kid.getvalue() < 100 then { delete(node.children, k) next } if kid.name === ("doc"|"bin"|"plugins"|"asm") then { delete(node.children, k) next } if kid.name === ("lib") & node.name === "src" then { delete(node.children, k) next } k +:= 1 } # perform an n-kid sectioning every k := 1 to *node.children do { kid := node.children[k] y0 := yA + (yB-yA) * real(k-1) / *node.children y1 := yA + (yB-yA) * real(k) / *node.children render3Da(f, kid, x, y0, w, y1-y0, lw) } # layout buildings. if *node.buildings > 0 then { # Fg("pale blue") k := 1 every kid := !node.buildings do { y0 := yA + (yB-yA) * real(k-1) / *node.buildings y1 := yA + (yB-yA) * real(k) / *node.buildings if k%2=1 then { # odd on left/west ht := 2 # y1-y0-2; if ht<1 then ht:=1 wd := 2 # ... x0 := x+w/2-lw/2-wd-2 } else { # even on right/east ht := 2 # y1-y0-2; if ht<1 then ht:=1 wd := 2 # ... x0 := x+w/2 + lw/2 + 2 } render3DBuilding(f, kid, x0 * METERS_PER_PIXEL, 0.0, y0 * METERS_PER_PIXEL, "vertical") # FillRectangle(x0, y0, wd, ht) k +:= 1 } } } end procedure render2D(node, x:0, y:0, w, h, parents_lw:0) if node.name == "src" then write("src: ", node.getvalue()) if node.getvalue() = 0 then fail /w := WIDTH /h := HEIGHT if node.getvalue() === 0 then lw := 1 else lw := integer(log(node.getvalue(), &e)) - node.level*2 if lw < 1 then lw := 1 else lw +:= 1 mywidth := integer(pixel_unit * node.getvalue()) $define SCALER 2.0 if \show2D then WAttrib("linewidth=" || lw) if node.level % 2 = 1 then { # horizontal mymax := WIDTH mywidth := integer(mywidth * SCALER + parents_lw) if mywidth > mymax then mywidth := mymax xA := x+w/2-mywidth/2 xB := x+w/2+mywidth/2 if \show2D then { Fg(if node.lang==="C" then "brown" else "black") DrawLine(xA,y+h/2, xB,y+h/2) } if (node.level <= 3 | mywidth > 10) & lw > 1 then { if \show2D then { Fg("dark green") DrawString(x+w/2-mywidth/2,y+h/2-lw/2-4, node.name) Fg("black") } } # see if any kids need to be excluded k := 1 while k <= *node.children do { kid := node.children[k] if kid.getvalue() < 100 then { delete(node.children, k) next } if kid.name === ("doc"|"bin"|"plugins"|"asm") then { delete(node.children, k) next } if kid.name === ("lib") & node.name === "src" then { delete(node.children, k) next } k +:= 1 } # perform an n-kid sectioning every k := 1 to *node.children do { kid := node.children[k] x0 := xA + (xB-xA) * real(k-1) / *node.children x1 := xA + (xB-xA) * real(k) / *node.children render2D(kid, x0, y, x1-x0, h, lw) } # layout buildings. if *node.buildings > 0 then { if \show2D then { Fg("pale blue") } k := 1 every kid := !node.buildings do { x0 := xA + (xB-xA) * real(k-1) / *node.buildings x1 := xA + (xB-xA) * real(k) / *node.buildings if k%2=1 then { # odd on top wd := 2 # x1-x0-2; if wd<1 then wd:=1 ht := 2 # ... y0 := y+h/2-lw/2-ht-2 } else { # even on bottom wd := 2 # x1-x0-2; if wd<1 then wd:=1 ht := 2 # ... y0 := y+h/2 + lw/2 + 2 } if \show2D then { FillRectangle(x0, y0, wd, ht) } k +:= 1 } } } else { # vertical if \show2D then { mymax := HEIGHT-WAttrib("fheight")*2 } else { mymax := HEIGHT - 28*2 } mywidth := integer(mywidth * SCALER + parents_lw) if mywidth > mymax then mywidth := mymax yA := y+h/2-mywidth/2 yB := y+h/2+mywidth/2 if \show2D then { Fg(if node.lang==="C" then "brown" else "black") DrawLine(x+w/2,yA, x+w/2,yB) tw := TextWidth(node.name) } if node.level <= 3 | mywidth > 20 then { if \show2D then { Fg("dark green") DrawString(x+w/2-tw/2,y+h/2+mywidth/2+15, node.name) Fg("black") } } # see if any kids need to be excluded k := 1 while k <= *node.children do { kid := node.children[k] if kid.getvalue() < 100 then { delete(node.children, k) next } if kid.name === ("doc"|"bin"|"plugins"|"asm") then { delete(node.children, k) next } if kid.name === ("lib") & node.name === "src" then { delete(node.children, k) next } k +:= 1 } # perform an n-kid sectioning every k := 1 to *node.children do { kid := node.children[k] y0 := yA + (yB-yA) * real(k-1) / *node.children y1 := yA + (yB-yA) * real(k) / *node.children render2D(kid, x, y0, w, y1-y0, lw) } # layout buildings. if *node.buildings > 0 then { if \show2D then { Fg("pale blue") } k := 1 every kid := !node.buildings do { y0 := yA + (yB-yA) * real(k-1) / *node.buildings y1 := yA + (yB-yA) * real(k) / *node.buildings if k%2=1 then { # odd on left/west ht := 2 # y1-y0-2; if ht<1 then ht:=1 wd := 2 # ... x0 := x+w/2-lw/2-wd-2 } else { # even on right/east ht := 2 # y1-y0-2; if ht<1 then ht:=1 wd := 2 # ... x0 := x+w/2 + lw/2 + 2 } if \show2D then { FillRectangle(x0, y0, wd, ht) } k +:= 1 } } } end