Warning: this is an htmlized version!
The original is across this link,
and the conversion rules are here.
-- zrect.lua: the ZRect class for dednat6
-- This file:
-- http://angg.twu.net/dednat6/zrect.lua
-- http://angg.twu.net/dednat6/zrect.lua.html
--  (find-angg        "dednat6/zrect.lua")
--
-- ZRects are read from ascii files as rectangles, and they can be
-- converted into many other things - for example, we can latex them
-- by converting them into Picture objects.
-- Older version: (find-angg "LUA/picture.lua" "Rectdef")
--
-- Note: this is practically obsolete - it was used here,
--   (find-ist "-handouts.tex" "brute-force")
-- but not elsewhere, and I would like to rewrite to integrate it with
-- this:
--   (find-dn6 "newrect.lua" "asciirectpoints")


-- «.zrectdefs_get»		(to "zrectdefs_get")
-- «.zrectdefs_get-tests»	(to "zrectdefs_get-tests")
-- «.ZRect»			(to "ZRect")

-- «.getrect»		(to "getrect")
-- «.getrect-tests»	(to "getrect-tests")
-- «.getrectdefs»	(to "getrectdefs")
-- «.getrectdefs-tests»	(to "getrectdefs-tests")
-- «.Rectdef»		(to "Rectdef")
-- «.Rectdef-tests»	(to "Rectdef-tests")

require "picture"   -- (find-dn6 "picture.lua")



--                    _      _       __     
--  _____ __ ___  ___| |_ __| | ___ / _|___ 
-- |_  / '__/ _ \/ __| __/ _` |/ _ \ |_/ __|
--  / /| | |  __/ (__| || (_| |  __/  _\__ \
-- /___|_|  \___|\___|\__\__,_|\___|_| |___/
--                                          
-- «zrectdefs_get» (to ".zrectdefs_get")
--
-- We use zrectdefs to define ZSets, ZDAGs, Zfunctions, etc, in 2D
-- ascii diagrams embedded in LaTeX comments in "%R" blocks.
-- See: (find-dn6 "heads6.lua" "zrect-head")
--
-- A "zrectdef" is 4-uple: (name, lop, rlines, rop).
-- In the test string below, the zrectdefs are:
--   ("a", "2", {"  00  ", ...}, ""),
--   ("b", "1", {"a b c ", ...}, "foo"),
--   ("c", "2", {"    23    ", ...}, "bar"). 
--
zrectdefs_bigstr_for_tests = [[
%R a := 2/  00  \   b := 1/a b c \foo
%R       |10  01|         | d e f|
%R       \  00  /         \    g /
%R
%R c := 2/    23    \bar
%R       |  22  13
%R       |    12  03
%R       |  11  02
%R       |10  01
%R       \  00
%R
%R d := 2/  54	    \
%R       |53  44	   
%R 	 |  43  34   
%R 	 |    33	   
%R 	 |      23   
%R 	 |    22  13 
%R 	 |  21  12   
%R 	 |20  11	   
%R 	 |  10	   
%R 	 \    00     
%R
]]

zrectdefs_get1 = 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
zrectdefs_get = function (lines, i, j, f)
    f = f or zrectdef_f
    for k=i,(j or i) do
      local line = lines[k]
      local pat  = "(%w+) := ([^ /]*)/().-()\\([^ ]*)"
      for name,lop,p1,p2,rop in line:gmatch(pat) do
        local rlines = zrectdefs_get1(lines, k, p1, p2, name)
        f(name, lop, rlines, rop)
      end
    end
  end

-- What to do with each zrectdef
-- (i.e., good values for the "f" in for zrectdefs_get)
zrectdef_print = function (name, lop, rlines, rop)
    print(name.." := "..lop.."(")
    print(table.concat(rlines, "\n"))
    print(")"..rop)
  end

zrects = {}
zrectdef_store = function (name, lop, rlines, rop)
    zrects[name] = ZRect.from(name, lop, rlines, rop):setlrdata() -- see below
  end

zrectdef_f = zrectdef_store

-- «zrectdefs_get-tests» (to ".zrectdefs_get-tests")
--[==[
* (eepitch-lua51)
* (eepitch-kill)
* (eepitch-lua51)
dofile "zrect.lua"
lines = splitlines(untabify(zrectdefs_bigstr_for_tests))
zrectdefs_get(lines, 1, nil, PP)
zrectdef_f = PP
zrectdefs_get(lines, 5, nil)
zrectdefs_get(lines, 5, nil, zrectdef_print)

zrectdef_f = zrectdef_store
zrectdefs_get(lines, 1, 5)
= zrects.c

--]==]





--  _________           _          _               
-- |__  /  _ \ ___  ___| |_    ___| | __ _ ___ ___ 
--   / /| |_) / _ \/ __| __|  / __| |/ _` / __/ __|
--  / /_|  _ <  __/ (__| |_  | (__| | (_| \__ \__ \
-- /____|_| \_\___|\___|\__|  \___|_|\__,_|___/___/
--                                                 
-- «ZRect» (to ".ZRect")
-- ZRects are zrectdefs with extra information.
--
-- In a ZRect we can access the contents of each of its "cells" by its
-- coordinates, using several coordinate systems: row/col, X/y, x/y,
-- l/r. Several fields of a ZRect are variables (offsets, dimensions,
-- etc) that help in converting between those several coordinate
-- systems; other fields, like lop and rop, come from the zrectdef.
--
-- The easy conversions between coordinate systems are:
--   row/col <-> x/y <-> X/y <-> l/r
--   (row,col) := (nrows-y,x+1)
--   (col-1,nrows-row) =: (x,y)
--                        (x,y) := (X+x0,y)
--                        (x-x0,y) =: (X,y)
--                                    (X,y) := (r-l,r+l)
--                                    ((y-X)/2,(y+X)/2) =: (l,r)
--
ZRect = Class {
  type    = "ZRect",
  from    = function (name, lop, rlines, rop, w)
      w = w or lop:sub(-1)+0    -- use the last digit of op when w==nil
      return ZRect {
        name   = name,
        lop    = lop,
        rop    = rop,
        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,
  __tostring = function (zrect)
      return zrect:toascii()
    end,
  __index = {
    toascii0 = function (zrect) return table.concat(zrect.rlines, "\n") end,
    toascii  = function (zrect)
        return format("%s = %s[[\n%s\n]]%s",
                 zrect.name, zrect.lop, zrect:toascii0(), zrect.rop)
      end,
    --
    -- row/col coordinates
    rcget0 = function (zrect, row, col)
        local w = zrect.w
        return zrect.rlines[row]:sub((col-1)*w+1, col*w)
      end,
    rcget = function (zrect, row, col)
        if col < 1 then return end
        return (zrect:rcget0(row, col):match("%S+"))
      end,
    --
    -- calculate x0 (used to convert between x/y and X/y coordinates)
    getx0 = function (zrect)
        for x=0,zrect.maxx do
          if zrect:xyget(x, 0) then return x end
        end
      end,
    setx0 = function (zrect, x0)
        x0         = x0 or zrect:getx0()
        zrect.x0   = x0
        zrect.minX = - x0
        zrect.maxX = zrect.maxx - x0
        return zrect
      end,
    --
    xyget = function (zrect, x, y)
        local row = zrect.nrows - y
        local col = x + 1
        return zrect:rcget(row, col)
      end,
    Xyget = function (zrect, X, y, verbose)
        local x = X + zrect.x0
        return zrect:xyget(x, y)
      end,
    --
    -- l/r coordinates
    Xytolr = function (zrect, X, y)
        local l = (y - X) / 2
        local r = (y + X) / 2
        return l, r
      end,
    lrtoXy = function (zrect, l, r)
        local X = r - l
        local y = r + l
        return X, y
      end,
    lrget = function (zrect, l, r)
        return zrect:Xyget(zrect:lrtoXy(l, r))
      end,
    setlrdata0 = function (zrect)
        local zr = zrect                -- abbreviation
        zr.minl, zr.minr = 0, 0         -- constants
        zr.maxl, zr.maxr = 0, 0         -- modified below
        zr.minlfor, zr.maxlfor = {}, {}
        zr.minrfor, zr.maxrfor = {}, {}
        for y=0,zrect.maxy do
          for X=zrect.minX,zrect.maxX do
            if zrect:Xyget(X, y) then
              local l, r = zrect:Xytolr(X, y)
              zr.maxl = Max(zr.maxl, l)
              zr.maxr = Max(zr.maxr, r)
              zr.minlfor[r] = Min(zr.minlfor[r], l)
              zr.minrfor[l] = Min(zr.minrfor[l], r)
              zr.maxlfor[r] = Max(zr.maxlfor[r], l)
              zr.maxrfor[l] = Max(zr.maxrfor[l], r)
              -- PP {X=X, y=y, A=zrect:Xyget(X, y), l=l, r=r}
            end
          end
        end
        return zr
      end,
    setlrdata = function (zrect)
        return zrect:setx0():setlrdata0()
      end,
    --
    -- Standard ways to convert a ZRect to a Picture object
    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,
    --
    topicture = function (zrect, coordsys, opts, f)
        local method = "topicture_"..coordsys.."0"
        local p = zrect[method](zrect, opts, f)
        return p
      end,
    --
    -- Draw borders
    drawline0 = function (zr, p, xy, dxdy, len)
        local tex = format("\\put(%s){\\line(%s){%s}}\n", xy, dxdy, len)
        table.insert(p.other, tex)
      end,
    drawborders = function (zr, p)
        local l, r, oldl, oldr, delta
        local setl = function (newl) oldl, delta, l = l, newl-l, newl end
        local setr = function (newr) oldr, delta, r = r, newr-r, newr end
        local maxl = function (r) return zr.maxlfor[r] end
        local maxr = function (l) return zr.maxrfor[l] end
        local minl = function (r) return zr.minlfor[r] end
        local minr = function (l) return zr.minrfor[l] end
        --
        local south = function (l, r) return format("south(%d,%d)", l, r) end
        local west  = function (l, r) return format("west(%d,%d)", l, r) end
        local east  = function (l, r) return format("east(%d,%d)", l, r) end
        local north = function (l, r) return format("north(%d,%d)", l, r) end
        local nw, ne = "nw", "ne"
        local pushline = function (...) PP(...) end
        --
        local nw, ne = "-1,1", "1,1"
        local south = function (l, r) return {l=l-0.5, r=r-0.5} end
        local west  = function (l, r) return {l=l+0.5, r=r-0.5} end
        local east  = function (l, r) return {l=l-0.5, r=r+0.5} end
        local north = function (l, r) return {l=l+0.5, r=r+0.5} end
        local pushline = function (lr, dxdy, len)
            p:lrputline(lr.l, lr.r, dxdy, len)
          end
        --
        -- Left wall
        l, r = 0, 0
        setl(maxl(0))                       -- move nw
        pushline(south(0, 0), nw, delta+1)
        while l < zr.maxl do
          setr(minr(l+1))                   -- move ne
          pushline(west(l, oldr), ne, delta)
          setl(maxl(r))                     -- move nw
          pushline(west(oldl, r), nw, delta)
        end
        setr(zr.maxr)                       -- move ne
        pushline(west(l, oldr), ne, delta+1)
        --
        -- Right wall
        l, r = 0, 0
        setr(maxr(0))                       -- move ne
        pushline(south(0, 0), ne, delta+1)
        while r < zr.maxr do
          setl(minl(r+1))                   -- move nw
          pushline(east(oldl, r), nw, delta)
          setr(maxr(l))                     -- move ne
          pushline(east(l, oldr), ne, delta)
        end
        setl(zr.maxl)                       -- move nw
        pushline(east(oldl, r), nw, delta+1)
      end,
    drawcuts = function (zr, p, lcuts, rcuts)
        local maxl = function (r) return zr.maxlfor[r] end
        local maxr = function (l) return zr.maxrfor[l] end
        local minl = function (r) return zr.minlfor[r] end
        local minr = function (l) return zr.minrfor[l] end
        --
        local nw, ne = "-1,1", "1,1"
        local south = function (l, r) return {l=l-0.5, r=r-0.5} end
        local west  = function (l, r) return {l=l+0.5, r=r-0.5} end
        local east  = function (l, r) return {l=l-0.5, r=r+0.5} end
        local north = function (l, r) return {l=l+0.5, r=r+0.5} end
        local pushline = function (lr, dxdy, len)
            p:lrputline(lr.l, lr.r, dxdy, len)
          end
        --
        for c in lcuts:gmatch"." do
          local l = c+0
          if l < zr.maxl then
            local r1 = max(minr(l), minr(l+1))
            local r2 = min(maxr(l), maxr(l+1))
            pushline(west(l, r1), ne, r2-r1+1)
          end
        end
        for c in rcuts:gmatch"." do
          local r = c+0
          if r < zr.maxr then
            local l1 = max(minl(r), minl(r+1))
            local l2 = min(maxl(r), maxl(r+1))
            pushline(east(l1, r), nw, l2-l1+1)
          end
        end
      end,
  },
}



-- (find-es "lua5" "mult-as-comp")



-- «Rectdef-tests» (to ".Rectdef-tests")
--[==[
* (eepitch-lua51)
* (eepitch-kill)
* (eepitch-lua51)
dofile "zrect.lua"
lines = splitlines(untabify(zrectdefs_bigstr_for_tests))
zrectdefs_get(lines, 1, #lines, getrectdef_store)
PP(keys(zrects))
PP(zrects.c)
= zrects.c:toascii()
-- change to [[]]

rd = zrects.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 "zrect.lua"
lines = splitlines(untabify(zrectdefs_bigstr_for_tests))
zrectdefs_get(lines, 1, #lines)
bu = function ()     return "$\\bullet$"              end
ab = function (a, b) return format("$(%d,%d)$", a, b) end
rd = zrects.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 "zrect.lua"
lines = splitlines(untabify(zrectdefs_bigstr_for_tests))
zrectdefs_get(lines, 1, #lines, getrectdef_store)
rd = zrects.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"


* (eepitch-lua51)
* (eepitch-kill)
* (eepitch-lua51)
dofile "zrect.lua"
lines = splitlines(untabify(zrectdefs_bigstr_for_tests))
zrectdefs_get(lines, 1, #lines)
rd = zrects.d
p = rd:topicture_lr0()
rd:drawborders(p)
= p:totex()

rd = zrects.d
p = rd:topicture_lr0()
rd:drawcuts(p, "01", "2345")
= p:totex()



-- Old stuff (broken)

PP(zrects.d)
PP(zrects.d.op)
PP(zrects.d.op:sub(-1))
PP(zrects.d.op:sub(-1) + 0)

zrects.d.w     = 2
zrects.d.ncols = 5
zrects.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,zrects.d.nrows do
  for col=1,zrects.d.ncols do
    PP(row, col, getcellatrowcol(zrects.d, row, col))
  end
end

--]==]



-- Local Variables:
-- coding: raw-text-unix
-- End: