Warning: this is an htmlized version!
The original is here, and the conversion rules are here. |
-- This file: -- http://angg.twu.net/LUA/picture.lua -- http://angg.twu.net/LUA/picture.lua.html -- (find-angg "LUA/picture.lua") -- -- This file defines a class that generates LaTeX picture-mode -- diagrams, and that can also output them as 2D ascii diagrams. -- Eduardo Ochs, 2015mar01 -- -- This is mainly for ZDAGs. See: -- (find-angg "LUA/canvas3.lua") -- (find-sfcfile "") -- -- This is an old/obsolete version of: -- (find-dn6 "picture.lua") -- «.Picture» (to "Picture") -- «.Picture-tests» (to "Picture-tests") -- «.getrect» (to "getrect") -- «.getrect-tests» (to "getrect-tests") -- «.getrectdefs» (to "getrectdefs") -- «.getrectdefs-tests» (to "getrectdefs-tests") -- «.Rectdef» (to "Rectdef") -- «.Rectdef-tests» (to "Rectdef-tests") -- «.Texfile» (to "Texfile") -- «.Texfile-tests» (to "Texfile-tests") Min = function (a, b) return (a and b and min(a, b)) or a or b end Max = function (a, b) return (a and b and max(a, b)) or a or b end -- ____ _ _ _ -- | _ \(_) ___| |_ _ _ _ __ ___ ___| | __ _ ___ ___ -- | |_) | |/ __| __| | | | '__/ _ \ / __| |/ _` / __/ __| -- | __/| | (__| |_| |_| | | | __/ | (__| | (_| \__ \__ \ -- |_| |_|\___|\__|\__,_|_| \___| \___|_|\__,_|___/___/ -- -- «Picture» (to ".Picture") -- We can ":put" things one by one into a Picture object, and ":totex" -- will generate a "\begin{picture}...\end{picture}" LaTeX block with -- the right size and offset. Also, ":toascii" generates an ascii -- rendering of that picture object, great for debugging and stuff. Picture = Class { type = "Picture", new = function (opts) local p = {whats={}} -- start empty for k,v in pairs(opts or {}) do p[k] = v end -- copy options return Picture(p) -- set metatable end, __index = { put = function (p, x, y, what) p.minx = p.minx and min(x, p.minx) or x p.miny = p.miny and min(y, p.miny) or y p.maxx = p.maxx and max(p.maxx, x) or x p.maxy = p.maxy and max(p.maxy, y) or y table.insert(p.whats, {x=x, y=y, what=what}) return p end, lrput = function (p, l, r, what) local x = r - l local y = r + l p:put(x, y, what) return p end, togrid = function (p) local lines = {} for y=p.miny,p.maxy do lines[y] = {} end for _,what in ipairs(p.whats) do lines[what.y][what.x] = what.what end return lines end, toasciilines = function (p, whitespace) local asciilines = {} local grid = p:togrid() for y=p.miny,p.maxy do for x=p.minx,p.maxx do local ascii = grid[y][x] or whitespace or " " asciilines[y] = (asciilines[y] or "")..ascii end end return asciilines end, toascii = function (p, whitespace) local asciilines = p:toasciilines(whitespace) local lines = {} for y=p.maxy,p.miny,-1 do table.insert(lines, asciilines[y]) end return table.concat(lines, "\n") end, -- -- (find-es "tex" "dags") -- (find-kopkadaly4page (+ 12 289) "13.1.3 The positioning commands") -- (find-kopkadaly4text (+ 12 289) "13.1.3 The positioning commands") -- (find-kopkadaly4page (+ 12 298) "\\put") -- (find-kopkadaly4text (+ 12 298) "\\put") totex1 = function (p, what) -- local fmt = " \\put(%s,%s){%s}\n" local fmt = " \\put(%s,%s){\\hbox to 0pt{\\hss %s%s\\hss}}\n" return format(fmt, what.x, what.y, p.font or "", what.what) end, totexputs = function (p) local f = function (what) return p:totex1(what) end return mapconcat(f, p.whats) end, -- -- (find-kopkadaly4page (+ 12 301) "13.1.6 Shifting a picture environment") -- (find-kopkadaly4text "13.1.6 Shifting a picture environment") -- (find-texbookpage (+ 12 66) "\\raise") -- (find-texbooktext (+ 12 66) "\\raise") totex = function (p) local a = {} a.scale = p.scale or "1ex" a.xdimen = p.maxx - p.minx + 1 a.ydimen = p.maxy - p.miny + 1 a.xoffset = p.minx - 0.5 -- a.yoffset = (p.miny + p.maxy + 1) / 2 a.yoffset = p.miny a.lower = (p.maxy - p.miny) / 2 a.body = p:totexputs() local fmt = "{\\unitlength=!scale\n".. "\\lower!lower\\unitlength\\hbox{".. "\\begin{picture}(!xdimen,!ydimen)(!xoffset,!yoffset)\n".. "!body".. "\\end{picture}\n".. "}}" return (fmt:gsub("!([a-z]+)", a)) end, }, } -- «Picture-tests» (to ".Picture-tests") --[[ * (eepitch-lua51) * (eepitch-kill) * (eepitch-lua51) dofile "picture.lua" p = Picture {whats={}} p:put(2, 3, "23") p:put(4, 7, "47") PP(p) PP(p:togrid()) = p:toascii() = p:totexputs() p.scale = "10pt" = p:totex() -- (find-LATEX "tmp.tex") f1 = function (p, lr) local l = lr:sub(1,1)+0 local r = lr:sub(2,2)+0 p:lrput(l, r, lr) end f = function (p, str) for _,lr in ipairs(split(str)) do f1(p, lr) end end p = Picture {whats={}} f(p, "00 01 02 03 04") f(p, "10 11 12 13 14") f(p, "22 23 24") f(p, "32 33 34") = p:toascii() p.scale = "10pt" = p:totex() --]] -- _ _ -- __ _ ___| |_ _ __ ___ ___| |_ -- / _` |/ _ \ __| '__/ _ \/ __| __| -- | (_| | __/ |_| | | __/ (__| |_ -- \__, |\___|\__|_| \___|\___|\__| -- |___/ -- -- «getrect» (to ".getrect") -- "getrect" extracts one rectangle (plus its name and op). -- "getrectdefs1" finds all rectdefs in a line, and runs an "f" on each. -- Note: a "rectdef" is a triple: (name, op, rlines). -- getrect_bigstr_for_tests = [[ %R a := 2/ 00 \ b := 1/a b c \ %R |10 01| | d e f| %R \ 00 / \ g / %R %R c := 2/ 23 \ %R | 22 13 %R | 12 03 %R | 11 02 %R |10 01 %R \ 00 ]] getrect = function (lines, y1, p1, p2, name) for y2=y1+1,#lines+1 do -- note: y2 == #lines+1 local c = (lines[y2] or ""):sub(p1-1, p1-1) -- implies c == "" if c == "" then error("Rectangle '"..name.." :=' at line "..y1.. " has no lower left '\\'") elseif c == "\\" then local rlines = {} for y=y1,y2 do table.insert(rlines, lines[y]:sub(p1, p2-1)) end return rlines end end end getrectdefs1 = function (lines, y1, f) local line = lines[y1] -- was: local pat = "([a-z]+) := ([a-z]*)/().-()\\" local pat = "(%w+) := (%w*)/().-()\\" for name,op,p1,p2 in line:gmatch(pat) do local rect = getrect(lines, y1, p1, p2, name) f(name, op, rect) end end -- «getrect-tests» (to ".getrect-tests") --[==[ * (eepitch-lua51) * (eepitch-kill) * (eepitch-lua51) dofile "picture.lua" lines = splitlines(untabify(getrect_bigstr_for_tests)) getrectdefs1(lines, 1, PP) getrectdefs1(lines, 5, PP) --]==] -- «getrectdefs» (to ".getrectdefs") -- "getrectdefs" processes all things like "name := op/rect\" in -- lines[i..j], and runs "f" on each one (i.e., "on each rectdef"). -- getrectdefs = function (lines, i, j, f) for k=i,j do getrectdefs1(lines, k, f) end end -- "f"s for getrectdefs, to be used like this: -- getrectdefs(lines, 1, #lines, getrectdef_print) -- getrectdefs(lines, 1, #lines, getrectdef_store) getrectdef_print = function (name, op, rlines) print(name.." := "..op.."(") print(table.concat(rlines, "\n")) print(")") end rectdefs = {} getrectdef_store = function (name, op, rlines) if op == "1" then rectdefs[name] = Rectdef.from(name, op, rlines):setlrdata() elseif op == "2" then rectdefs[name] = Rectdef.from(name, op, rlines):setlrdata() else getrectdef_print(name, op, rlines) error("Uknown op:", op) end end -- «getrectdefs-tests» (to ".getrectdefs-tests") --[==[ * (eepitch-lua51) * (eepitch-kill) * (eepitch-lua51) dofile "picture.lua" lines = splitlines(untabify(getrect_bigstr_for_tests)) getrectdefs(lines, 1, #lines, PP) getrectdefs(lines, 1, #lines, getrectdef_print) getrectdefs(lines, 1, #lines, getrectdef_store) PP(rectdefs.c) --]==] -- ____ _ _ __ _ -- | _ \ ___ ___| |_ __| | ___ / _| ___| | __ _ ___ ___ -- | |_) / _ \/ __| __/ _` |/ _ \ |_ / __| |/ _` / __/ __| -- | _ < __/ (__| || (_| | __/ _| | (__| | (_| \__ \__ \ -- |_| \_\___|\___|\__\__,_|\___|_| \___|_|\__,_|___/___/ -- -- «Rectdef» (to ".Rectdef") -- Support for rectangular objects - e.g., functions on ZDAGs - -- embedded in TeX comments -- Rectdef = Class { type = "Rectdef", from = function (name, op, rlines, w) w = w or op:sub(-1)+0 -- use the last digit of op when w==nil return Rectdef { name = name, op = op, w = w, rlines = rlines, nrows = #rlines, ncols = #rlines[1] / w, minx = 0, -- constant miny = 0, -- constant maxy = #rlines - 1, maxx = (#rlines[1] / w) - 1, } end, __index = { toascii0 = function (rd) return table.concat(rd.rlines, "\n") end, toascii = function (rd) return format("%s = %s[[\n%s\n]]", rd.name, rd.op, rd:toascii0()) end, -- rcget0 = function (rectdef, row, col) local w = rectdef.w return rectdef.rlines[row]:sub((col-1)*w+1, col*w) end, rcget = function (rectdef, row, col) return (rectdef:rcget0(row, col):match("%S+")) end, getx0 = function (rectdef) for x=0,rectdef.maxx do if rectdef:xyget(x, 0) then return x end end end, setx0 = function (rectdef, x0) x0 = x0 or rectdef:getx0() rectdef.x0 = x0 rectdef.minX = - x0 rectdef.maxX = rectdef.maxx - x0 return rectdef end, -- xyget = function (rectdef, x, y) local row = rectdef.nrows - y local col = x + 1 return rectdef:rcget(row, col) end, Xyget = function (rectdef, X, y, verbose) local x = X + rectdef.x0 if verbose then PP {X=X, x=x, y=y, z=rectdef:xyget(x, y)} end -- dbg return rectdef:xyget(x, y) end, Xytolr = function (rectdef, X, y) local l = (y - X) / 2 local r = (y + X) / 2 return l, r end, lrtoXy = function (rectdef, l, r) local X = r - l local y = r + l return X, y end, lrget = function (rectdef, l, r) return rectdef:Xyget(rectdef:lrtoXy(l, r)) end, setlrdata0 = function (rectdef) local rd = rectdef -- abbreviation rd.minl, rd.minr = 0, 0 -- constants rd.maxl, rd.maxr = 0, 0 -- modified below rd.minlfor, rd.maxlfor = {}, {} rd.minrfor, rd.maxrfor = {}, {} for y=0,rectdef.maxy do for X=rectdef.minX,rectdef.maxX do if rectdef:Xyget(X, y) then local l, r = rectdef:Xytolr(X, y) rd.maxl = Max(rd.maxl, l) rd.maxr = Max(rd.maxr, r) rd.minlfor[r] = Min(rd.minlfor[r], l) rd.minrfor[l] = Min(rd.minrfor[l], r) rd.maxlfor[r] = Max(rd.maxlfor[r], l) rd.maxrfor[l] = Max(rd.maxrfor[l], r) -- PP {X=X, y=y, A=rectdef:Xyget(X, y), l=l, r=r} end end end return rd end, setlrdata = function (rectdef) return rectdef:setx0():setlrdata0() end, -- topicture_xy0 = function (rd, opts, f) f = f or function (x, y, s) return s end local p = Picture.new(opts) for y=rd.maxy,0,-1 do for x=0,rd.maxx do if rd:xyget(x, y) then p:put(x, y, f(x, y, rd:xyget(x, y))) end end end return p end, topicture_Xy0 = function (rd, opts, f) f = f or function (X, y, s) return s end local p = Picture.new(opts) for y=rd.maxy,0,-1 do for X=rd.minX,rd.maxX do if rd:Xyget(X, y) then p:put(X, y, f(X, y, rd:Xyget(X, y))) end end end return p end, topicture_lr0 = function (rd, opts, f) f = f or function (l, r, s) return s end local p = Picture.new(opts) for l=0,rd.maxl do for r=0,rd.maxr do if rd:lrget(l, r) then p:lrput(l, r, f(l, r, rd:lrget(l, r))) end end end return p end, }, } -- «Rectdef-tests» (to ".Rectdef-tests") --[==[ * (eepitch-lua51) * (eepitch-kill) * (eepitch-lua51) dofile "picture.lua" lines = splitlines(untabify(getrect_bigstr_for_tests)) getrectdefs(lines, 1, #lines, getrectdef_store) PP(keys(rectdefs)) PP(rectdefs.c) = rectdefs.c:toascii() -- change to [[]] rd = rectdefs.c p = Picture {whats={}, scale="10pt"} p = Picture {whats={}, scale="9pt"} p = Picture {whats={}, scale="7pt", font="\\footnotesize "} p = Picture.new {scale="10pt"} p = Picture.new {scale="9pt"} p = Picture.new {scale="7pt", font="\\footnotesize "} for l=0,rd.maxl do for r=0,rd.maxr do if rd:lrget(l, r) then -- PP(l, r, rd:lrget(l, r)) p:lrput(l, r, rd:lrget(l, r)) end end end = p:toascii(" ") = p:toascii("..") = p:totex() * (eepitch-lua51) * (eepitch-kill) * (eepitch-lua51) dofile "picture.lua" lines = splitlines(untabify(getrect_bigstr_for_tests)) getrectdefs(lines, 1, #lines, getrectdef_store) bu = function () return "$\\bullet$" end ab = function (a, b) return format("$(%d,%d)$", a, b) end rd = rectdefs.c = rd:topicture_lr0 {scale="7pt", font="\\footnotesize "} :totex() = rd:topicture_lr0({scale="4pt", font="\\footnotesize "}, bu):totex() = rd:topicture_xy0({scale="4pt", font="\\footnotesize "}, ab):totex() = rd:topicture_Xy0({scale="4pt", font="\\footnotesize "}, ab):totex() = rd:toascii() = rd:topicture_lr0( ):toascii(" ") = rd:topicture_lr0( ):toascii("..") = rd:topicture_lr0( ):totex() = rd:topicture_lr0({} ):totex() = rd:topicture_lr0({}, bu):totex() = rd:topicture_xy0({}, ab):totex() = rd:topicture_Xy0({}, ab):totex() oc = {scale="12pt", font="\\footnotesize "} o2 = {scale="7pt", font="\\footnotesize "} ob = {scale="4pt", font="\\footnotesize "} = rd:topicture_lr0(ob, bu):totex(), rd:topicture_lr0(o2 ):totex(), rd:topicture_lr0(oc, ab):totex(), rd:topicture_xy0(oc, ab):totex(), rd:topicture_Xy0(oc, ab):totex() s = rd:topicture_lr0(ob, bu):lrput(0.5, 0, "$\\searrow$"):totex().."\n".. rd:topicture_lr0(o2 ):totex().."\n".. rd:topicture_lr0(oc, ab):totex().."\n".. rd:topicture_xy0(oc, ab):totex().."\n".. rd:topicture_Xy0(oc, ab):totex() = s writefile("/tmp/o.tex", s) -- (find-fline "/tmp/o.tex") -- (kill-new "$$\\input /tmp/o.tex $$") -- (find-angg "LATEX/tmp.tex") * (eepitch-lua51) * (eepitch-kill) * (eepitch-lua51) dofile "picture.lua" lines = splitlines(untabify(getrect_bigstr_for_tests)) getrectdefs(lines, 1, #lines, getrectdef_store) rd = rectdefs.c d = function (e) PP(e, expr(e)) end ds = function (es) for _,e in ipairs(split(es)) do d(e) end end = rd:toascii() ds "rd.minlfor rd.minrfor" ds "rd.maxlfor rd.maxrfor" -- Old stuff PP(rectdefs.d) PP(rectdefs.d.op) PP(rectdefs.d.op:sub(-1)) PP(rectdefs.d.op:sub(-1) + 0) rectdefs.d.w = 2 rectdefs.d.ncols = 5 rectdefs.d.nrows = 6 getcellatrowcol = function (rectdef, row, col) local w = rectdef.w return rectdef.lines[row]:sub((col-1)*w+1, col*w) end for row=1,rectdefs.d.nrows do for col=1,rectdefs.d.ncols do PP(row, col, getcellatrowcol(rectdefs.d, row, col)) end end --]==] -- _____ __ _ _ _ -- |_ _|____ __/ _(_) | ___ ___| | __ _ ___ ___ -- | |/ _ \ \/ / |_| | |/ _ \ / __| |/ _` / __/ __| -- | | __/> <| _| | | __/ | (__| | (_| \__ \__ \ -- |_|\___/_/\_\_| |_|_|\___| \___|_|\__,_|___/___/ -- -- «Texfile» (to ".Texfile") -- Code for reading ".tex" files and processing their comment blocks -- up to the current line. -- Note that there is no "Texfile" class yet... -- ...except for this draft: heads = {} registerhead = function (headstr) return function (head) head.headstr = headstr heads[headstr] = head end end registerhead "" {} registerhead "%:" {name="tree"} registerhead "%L" {name="lua"} registerhead "%D" {name="diag"} registerhead "%R" {name="rect"} registerhead "%:\15" {name="abbrev"} --[[ * (eepitch-lua51) * (eepitch-kill) * (eepitch-lua51) dofile "picture.lua" -- (find-LATEX "2008dclosed.tex") tf = TexFile.read "~/LATEX/2008dclosed.tex" while tf.nline <= #tf.lines do tf:getblock() local nb = #tf.blocks PP(nb, tf.blocks[nb]) tf:advance() end --]] TexFile = Class { type = "TexFile", new = function (name, bigstr) return TexFile({name=name}):setlines(bigstr) end, read = function (fname) return TexFile.new(fnamenondirectory(fname), ee_readfile(fname)) end, __index = { setlines = function (tf, bigstr) tf.lines = splitlines(bigstr) tf.lines.abbrev = {} tf.lines.begriff = {} tf.lines.diag = {} tf.lines.lua = {} tf.lines.tree = {} tf.lines.zrect = {} tf.blocks = {} tf.nline = 1 return tf end, head = function (tf, i) local li = tf.lines[i or tf.nline] local p = function (len) return heads[li:sub(1, len)] end return p(3) or p(2) or p(1) or p(0) end, nexthead = function (tf) return tf:head(tf.nline + 1) end, hasnext = function (tf) return tf.nline < #(tf.lines) end, advance = function (tf) tf.nline = tf.nline + 1 end, getij = function (tf) local i = tf.nline tf.thishead = tf:head() while tf:hasnext() and tf:nexthead() == tf.thishead do tf:advance() end local j = tf.nline return i,j end, getrest = function (tf, i) local headstr = tf.thishead.headstr return untabify(tf.lines[i]):sub(#headstr+2) end, getblock = function (tf) local i,j = tf:getij() local name = tf.thishead.name local rests = {} for k=i,j do local rest = tf:getrest(k) if tf.lines[name] then tf.lines[name][k] = rest end table.insert(rests, rest) end table.insert(tf.blocks, {i=i, j=j, name=name}) return i,j,rests end, }, } tf_prefixes = { -- ["%"] = "comment", ["%:"] = "tree", ["%L"] = "lua", ["%D"] = "diag", ["%R"] = "rect", ["%:\15"] = "abbrev", } -- Right now only one texfile is supported... -- See tf_update, below. tf_name = nil tf_lines = nil tf_blocks = {} tf_lines_tree = {} tf_lines_lua = {} tf_lines_diag = {} tf_lines_rect = {} tf_lines_abbrev = {} tf_i = 1 tf_parse0 = function (line) for len=3,1,-1 do local p = line:sub(1, len) -- PP(len, p) local name = tf_prefixes[p] if name then local u = untabify(line):sub(len+2) return p, name, u end end end tf_parse1 = function (n) local line = tf_lines[n] if not line then return end local p, name, u = tf_parse0 (line) if name then local tf_lines_name = _G["tf_lines_"..name] tf_lines_name[n] = u return name end end tf_parse2 = function (i) local name = tf_parse1(i) if name then local j = i+1 while name == tf_parse1(j) do j = j + 1 end return j, name, i, j-1 else return i+1 end end tf_parse_block = function (name, i, j) table.insert(tf_blocks, {i=i, j=j, name=name}) PP(i, j, name) if name == "rect" then print("Rect:") print(table.concat(tf_lines, "\n", i, j)) getrectdefs(tf_lines, i, j, getrectdef_store) elseif name == "lua" then print("Lua:") local code = table.concat(tf_lines_lua, "\n", i, j) print(code) eval(code) end end tf_parse3 = function (n) n = n or #tf_lines while tf_i < n do local tf_newi, name, i, j = tf_parse2(tf_i) if name then tf_parse_block(name, i, j) end tf_i = tf_newi end end tf_update0 = function (jobname, lineno) if not tf_lines then -- quick hack tf_name = jobname tf_lines = splitlines(readfile(jobname..".tex")) end tf_parse3(lineno) end tf_update = function () -- for LuaTeX -- See (find-es "luatex" "tex.inputlineno") tf_update0(tex.jobname, tex.inputlineno) end -- «Texfile-tests» (to ".Texfile-tests") --[[ * (eepitch-lua51) * (eepitch-kill) * (eepitch-lua51) dofile "picture.lua" -- (find-LATEX "2008dclosed.tex") tf_lines = splitlines(ee_readfile "~/LATEX/2008dclosed.tex") tf_i = 1 tf_parse3(20) = tf_i tf_parse3(70) = tf_i tf_parse3(90) = tf_i tf_parse3() = tf_i, #tf_lines PP(tf_blocks) for i=1,#tf_lines do if tf_lines_lua[i] then print(i, tf_lines_lua[i]) end end f = function (line) PP(tf_parse0(line)) end f "%D hello" f "%L hello" f "%L " f "%L" f "% hello" --]] -- Local Variables: -- coding: raw-text-unix -- End: