|
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: