Warning: this is an htmlized version!
The original is here, and
the conversion rules are here.
-- This file:
--   http://angg.twu.net/LATEX/2022pict2e.lua.html
--   http://angg.twu.net/LATEX/2022pict2e.lua
--           (find-angg "LATEX/2022pict2e.lua")
-- Author: Eduardo Ochs <eduardoochs@gmail.com>
--
-- (defun l  () (interactive) (find-angg "LATEX/2022pict2e.lua"))
-- (defun e  () (interactive) (find-angg "LATEX/2022pict2e.tex"))
-- (defun eb () (interactive) (find-angg "LATEX/2022pict2e-body.tex"))
-- (defun o  () (interactive) (find-angg "LATEX/2021pict2e.lua"))
-- (defun v  () (interactive) (find-pdftools-page "~/LATEX/2022pict2e.pdf"))
-- (defun tb () (interactive) (find-ebuffer (eepitch-target-buffer)))
-- (defun etv () (interactive) (find-wset "13o2_o_o" '(tb) '(v)))

-- «.Show»			(to "Show")
-- «.Show-tests»		(to "Show-tests")
--
-- «.Pict2e»			(to "Pict2e")
-- «.Pict2e-tests»		(to "Pict2e-tests")
-- «.Pict2e-test-nums»		(to "Pict2e-test-nums")
-- «.Pict2eVector»		(to "Pict2eVector")
-- «.Pict2eVector-tests»	(to "Pict2eVector-tests")
-- «.Points2»			(to "Points2")
-- «.Points2-tests»		(to "Points2-tests")
-- «.PictBounds»		(to "PictBounds")
-- «.PictBounds-tests»		(to "PictBounds-tests")
--
-- «.PwSpec»			(to "PwSpec")
-- «.PwSpec-tests»		(to "PwSpec-tests")
-- «.PwFunction»		(to "PwFunction")
-- «.PwFunction-Riemann»	(to "PwFunction-Riemann")
-- «.PwFunction-tests»		(to "PwFunction-tests")
--
-- «.Expr»			(to "Expr")
-- «.Expr-tests»		(to "Expr-tests")
--
-- «.V3»			(to "V3")
-- «.V3-tests»			(to "V3-tests")
-- «.Surface0»			(to "Surface0")
-- «.Surface0-tests»		(to "Surface0-tests")
-- «.Surface»			(to "Surface")
-- «.Surface-tests»		(to "Surface-tests")

loaddednat6("dednat6/")




--  _____         _   
-- |_   _|__  ___| |_ 
--   | |/ _ \/ __| __|
--   | |  __/\__ \ |_ 
--   |_|\___||___/\__|
--                    
-- «Show»  (to ".Show")
-- Show a chunk of tex code by saving it to 2022pict2e-body.tex,
-- latexing 2022pict2e.tex, and displaying the resulting PDF.

Show = Class {
  type = "Show",
  new  = function (o) return Show {bigstr = tostring(o)} end,
  try  = function (bigstr) return Show.new(bigstr):write():compile() end,
  __tostring = function (test)
      return format("Show: %s => %s", test.fname_body, test.success or "?")
    end,
  __index = {
    fname_body  = "~/LATEX/2022pict2e-body.tex",
    fname_tex   = "~/LATEX/2022pict2e.tex",
    --        (find-LATEX "2022pict2e.tex")
    --
    write = function (test)
        ee_writefile(test.fname_body, test.bigstr)
        return test
      end,
    cmd = function (test)
        local cmd = "cd ~/LATEX/ && lualatex "..test.fname_tex.." < /dev/null"
        return cmd
      end,
    compile = function (test)
        local log = getoutput(test:cmd())
        local success = log:match "Success!!!"
        Show.log = log
        test.success = success 
        return test
      end,
    print = function (test) print(test); return test end,
  },
}

-- «Show-tests»  (to ".Show-tests")
--[==[
* (eepitch-lua51)
* (eepitch-kill)
* (eepitch-lua51)
dofile "2022pict2e.lua"
= Show.try "Hello"
* (etv)
= Show.try [[$$ \ln x $$]]
* (etv)

* (eepitch-lua51)
* (eepitch-kill)
* (eepitch-lua51)
dofile "2022pict2e.lua"
* (etv)
= Show.try()
* (etv)

--]==]




--  ____  _      _   ____      
-- |  _ \(_) ___| |_|___ \ ___ 
-- | |_) | |/ __| __| __) / _ \
-- |  __/| | (__| |_ / __/  __/
-- |_|   |_|\___|\__|_____\___|
--                             
-- «Pict2e»  (to ".Pict2e")
--
Pict2e = Class {
  type = "Pict2e",
  new  = function () return Pict2e {lines={}, pb=nil} end,
  from = function (o)
      if otype(o) == "Pict2e" then
        return Pict2e {lines = copy(o.lines), pb = o.pb}
      end
      if type(o)  == "string" then return Pict2e {lines = splitlines(o)} end
      if type(o)  == "table"  then return Pict2e {lines = copy(o)} end
      error("From?")
    end,
  Line   = function (...) return Points2({...}):Line() end,
  bounds = function (...) return Pict2e.new():setbounds(...) end,
  bgat   = function (...) return Pict2e.bounds(...):grid():axesandticks() end,
  unitlength = function (str) return Pict2e.new():Unitlength(str) end,
  --
  sw = v(-2,-1),
  ne = v(6,4),
  --
  __tostring = function (p) return p:tostring() end,
  --
  __mul = function (prefix, p)
      local f = function (li) return prefix..li end
      return Pict2e {lines = map(f, p.lines), pb = p.pb}
    end,
  __add = function (a, b)
      a = Pict2e.from(a)
      b = Pict2e.from(b)
      a.pb = a.pb or b.pb
      for _,li in ipairs(b.lines) do
        table.insert(a.lines, li)
      end
      return a
    end,
  --
  __index = {
    --
    -- Functions to convert a Pict2e object to a string.
    -- Inside dednat6 it is better to make suffix = "%".
    --
    suffix = "",
    tolatex = function (p) return p:tostring("%") end,
    tostring = function (p, suffix)
        local f = function (li) return li..(suffix or p.suffix) end
        return mapconcat(f, p.lines, "\n")
      end,
    output = function (p) output(p:tolatex()); return p end,
    --
    -- Append lines.
    add = function (p, p2)
        for _,line in ipairs(Pict2e.from(p2).lines) do
          table.insert(p.lines, line)
        end
        return p
      end,
    --
    beginend = function (p, str1, str2) return str1 + ("  " * p) + str2   end,
    wrapin   = function (p, str1, str2) return str1 + ("  " * (p + str2)) end,
    as     = function (p, str) return p:wrapin("{"..str, "}") end,
    color  = function (p, color) return p:as("\\color{"..color.."}") end,
    grid_  = "\\color{GrayPale}\\linethickness{0.3pt}",
    axes_  = "\\linethickness{0.5pt}",
    asgrid = function (p) return p:as(p.grid_) end,
    asaxes = function (p) return p:as(p.axes_) end,
    def    = function (p, name) return p:wrapin("\\def\\"..name.."{", "}") end,
    --
    bep    = function (p) return p:beginend(p.pb:beginpicture(), "\\end{picture}") end,
    bepc   = function (p) return p:bep():wrapin("\\myvcenter{", "}") end,
    bepcb  = function (p) return p:bepc():wrapin("\\bhbox{$", "$}") end,
    --
    -- Set or change the PictBounds field.
    setbounds = function (p, ab, cd, e)
        ab,cd = ab or Pict2e.sw, cd or Pict2e.ne
        p.pb = PictBounds.new(ab, cd, e)
        return p
      end,
    --
    -- Append grids, axes, and ticks to an existing Pict2e object.
    grid0         = function (p) return p.pb:grid() end,
    grid          = function (p) return p:add(p:grid0():asgrid()) end,
    axesandticks0 = function (p) return p.pb:axesandticks() end,
    axesandticks  = function (p) return p:add(p:axesandticks0():asaxes()) end,
    grat          = function (p) return p:grid():axesandticks() end,
    --
    -- Append other things.
    Line       = function (p, ...) return p:add(Pict2e.Line(...)) end,
    Thick      = function (p, str) return p:add("\\linethickness{"..str.."}") end,
    Unitlength = function (p, str) return p:add("\\unitlength="..str) end,
    put     = function (p, xy, str) return p:add(pformat("\\put%s{%s}", xy, str)) end,
    putcell = function (p, xy, str) return p:put(xy, "\\cell{"..str.."}") end,
    puttext = function (p, xy, str) return p:putcell(xy, "\\text{"..str.."}") end,
    run = function (p, f) f(p); return p end,
    --
    show = function (p) return Show.try(p:tolatex()) end,
    bshow = function (p, sw, ne)
        -- local sw = sw or Pict2e.sw
        -- local ne = ne or Pict2e.ne
        return Pict2e.bounds(sw, ne):grid():axesandticks():add(p):bep():show()
      end,
    b0show = function (p, sw, ne)
        -- local sw = sw or Pict2e.sw
        -- local ne = ne or Pict2e.ne
        return Pict2e.bounds(sw, ne):add(p):bep():show()
      end,
  },
}



-- «Pict2e-tests»  (to ".Pict2e-tests")
--[[
* (eepitch-lua51)
* (eepitch-kill)
* (eepitch-lua51)
dofile "2022pict2e.lua"
= Pict2e.from               "A"
= Pict2e.from              {"A", "B"}
= Pict2e.from( Pict2e.from {"A", "B"} )

p = Pict2e.from({"aa", "bbb"})
=              p
=              p :tostring("%!")
=       "  " * p
=          p + p
=              p + "cccc"
=              p + {"cccc", "dd"}
=      "00"  + p
= {"1", "2"} + p

= p:beginend("\\begin{foo}", "\\end{foo}")
= p:wrapin  ("\\begin{foo}", "\\end{foo}")
= p:color("red")
= p:asgrid()
= p:asaxes()

p = Pict2e.from({"aa", "bbb", "cccc"})
PPPV(p)
= p:tostring("%")
= p:tostring("")
= p
= p:color("red")
= p

= Pict2e.Line(v(1,2), v(3,4)):Line(v(0,1), v(2,3), v(4,5))

* (eepitch-lua51)
* (eepitch-kill)
* (eepitch-lua51)
dofile "2022pict2e.lua"

p = Pict2e.bounds(v(0,0), v(3,2))
= p.pb
= p:Line(v(0,1), v(2,3)):color("red")
= p:Line(v(0,1), v(2,3)):color("red").pb

= Pict2e.bounds(v(0,0), v(3,2)):grid():axesandticks()
= Pict2e.bounds(v(0,0), v(3,2)):grid():axesandticks():bep()
= Pict2e.bounds(v(0,0), v(3,2)):grid():axesandticks():bep():show()
* (etv)
= Pict2e.bounds(v(-2,-1), v(6,4)):grid():axesandticks():bep():show()
* (etv)

--]]

-- «Pict2e-test-nums»  (to ".Pict2e-test-nums")
--[[
* (eepitch-lua51)
* (eepitch-kill)
* (eepitch-lua51)
dofile "2022pict2e.lua"
Pict2e.sw = v(-3,-3)
Pict2e.nw = v(3,3)
prep  = Pict2e.unitlength("12pt"):add("\\celllower=3pt")
pnums = Pict2e.bounds():axesandticks():color("gray")
pnums:run(function (p)
    p:puttext(v(0,2), "-6")
    p:puttext(v(0,1), "-4")
    p:puttext(v(1,1), "-2")
  end)
=         pnums
=         pnums:bep()
=  prep + pnums:bep()
= (prep + pnums:bep()):show()
* (etv)

--]]



--  ____  _      _   ____    __     __        _             
-- |  _ \(_) ___| |_|___ \ __\ \   / /__  ___| |_ ___  _ __ 
-- | |_) | |/ __| __| __) / _ \ \ / / _ \/ __| __/ _ \| '__|
-- |  __/| | (__| |_ / __/  __/\ V /  __/ (__| || (_) | |   
-- |_|   |_|\___|\__|_____\___| \_/ \___|\___|\__\___/|_|   
--                                                          
-- «Pict2eVector»  (to ".Pict2eVector")
-- Based on: (find-dn6 "picture.lua" "pict2evector-test")

Pict2eVector = Class {
  type     = "Pict2eVector",
  lowlevel = function (x0, y0, x1, y1)
      local dx, dy = x1-x0, y1-y0
      local absdx, absdy = math.abs(dx), math.abs(dy)
      local veryvertical = absdy > 100*absdx
      local f = function (Dx,Dy,len)
          return Dx,Dy, len
          -- pformat("\\put(%s,%s){\\vector(%s,%s){%s}}", x0,y0, Dx,Dy, len)
        end
      if veryvertical then
        if dy > 0 then return f( 0,1,  dy) else return f( 0,-1, -dy) end 
      else
        if dx > 0 then return f(dx,dy, dx) else return f(dx,dy, -dx) end
      end 
    end,
  --
  eps = 1/4,
  latex = function (x0, y0, x1, y1)
      local norm = math.sqrt((x1-x0)^2 + (y1-y0)^2)
      if norm < Pict2eVector.eps then
        return pformat("\\put%s{}", v(x0,y0)) -- if very short draw nothing
      end
      local Dx,Dy, len = Pict2eVector.lowlevel(x0, y0, x1, y1)
      return pformat("\\put%s{\\vector%s{%s}}", v(x0,y0), v(Dx,Dy), len)
    end,
  fromto = function (x0y0, x1y1)
      local x0,y0, x1,y1 = x0y0[1],x0y0[2], x1y1[1],x1y1[2]
      return Pict2e.from(Pict2eVector.latex(x0,y0, x1,y1))
    end,
  fromwalk = function (x0y0, dxdy)
      PP(x0y0, dxdy)
      return Pict2eVector.fromto(x0y0, x0y0+dxdy)
    end,
  __index = {
  },
}

-- «Pict2eVector-tests»  (to ".Pict2eVector-tests")
--[[
* (eepitch-lua51)
* (eepitch-kill)
* (eepitch-lua51)
loaddednat6()
dofile "2022pict2e.lua"
x0y0 = v(3,2)
= x0y0
f = function (ang, len)
    return Pict2eVector.fromwalk(x0y0, v(math.cos(ang),math.sin(ang))*len)
  end
= f(0, 2)

p = Pict2e.bounds(v(0,0), v(5,4)):grid():axesandticks()
-- for i=0,2,1/8 do p:add(f(i*math.pi, i)) end
-- for i=0,1,1/8 do p:add(f(i*math.pi, i)) end
for i=0,1/2,1/8 do p:add(f(i*math.pi, i)) end
= p
= p:bep():show()
* (etv)

--]]




--  ____       _       _       
-- |  _ \ ___ (_)_ __ | |_ ___ 
-- | |_) / _ \| | '_ \| __/ __|
-- |  __/ (_) | | | | | |_\__ \
-- |_|   \___/|_|_| |_|\__|___/
--                             
-- «Points2»  (to ".Points2")
--
Points2 = Class {
  type = "Points2",
  new  = function () return Points2 {} end,
  __tostring = function (pts) return pts:tostring() end,
  __index = {
    tostring = function (pts, sep)
        return mapconcat(tostring, pts, sep or "")
      end,
    add = function (pts, pt)
        table.insert(pts, pt)
        return pts
      end,
    adds = function (pts, pts2)
        for _,pt in ipairs(pts2) do table.insert(pts, pt) end
        return pts
      end,
    rev = function (pts)
        local pr = Points2.new()
        for i=#pts,1,-1 do
          table.insert(pr, pts[i])
        end
        return pr
      end,
    --
    pict2e = function (pts, prefix)
        return Pict2e.from(prefix .. tostring(pts))
      end,
    Line    = function (pts) return pts:pict2e("\\Line") end,
    polygon = function (pts) return pts:pict2e("\\polygon") end,
    region0 = function (pts) return pts:pict2e("\\polygon*") end,
    region  = function (pts, color) return pts:region0():color(color) end,
    --
    polygon = function (pts, s) return pts:pict2e("\\polygon"..(s or "")) end,
  },
}

-- «Points2-tests»  (to ".Points2-tests")
--[[
* (eepitch-lua51)
* (eepitch-kill)
* (eepitch-lua51)
dofile "2022pict2e.lua"
pts = Points2 {v(1,2), v(3,4), v(5,2)}
= pts
= pts:Line()
= pts:rev()
= pts:add(pts:rev()):region("red")

pts = Points2 {v(1,2), v(3,4), v(5,2)}
= pts:region("red"):bshow()
* (etv)
= pts:Line():bshow()
* (etv)

--]]



--  ____  _      _   ____                        _     
-- |  _ \(_) ___| |_| __ )  ___  _   _ _ __   __| |___ 
-- | |_) | |/ __| __|  _ \ / _ \| | | | '_ \ / _` / __|
-- |  __/| | (__| |_| |_) | (_) | |_| | | | | (_| \__ \
-- |_|   |_|\___|\__|____/ \___/ \__,_|_| |_|\__,_|___/
--                                                     
-- «PictBounds»  (to ".PictBounds")
-- (find-LATEX "edrxpict.lua" "pictp0-pictp3")
-- (find-es "pict2e" "picture-mode")
-- (find-kopkadaly4page (+ 12 288) "\\begin{picture}(x dimen,y dimen)")
-- (find-kopkadaly4text (+ 12 288) "\\begin{picture}(x dimen,y dimen)")
-- (find-kopkadaly4page (+ 12 301) "13.1.6 Shifting a picture environment")
-- (find-kopkadaly4text (+ 12 301) "13.1.6 Shifting a picture environment")
-- (find-kopkadaly4page (+ 12 302) "\\begin{picture}(x dimen,y dimen)(x offset,y offset)")
-- (find-kopkadaly4text (+ 12 302) "\\begin{picture}(x dimen,y dimen)(x offset,y offset)")

PictBounds = Class {
  type = "PictBounds",
  new  = function (ab, cd, e)
      local a,b = ab[1], ab[2]
      local c,d = cd[1], cd[2]
      local x1,x2 = min(a,c), max(a,c)
      local y1,y2 = min(b,d), max(b,d)
      return PictBounds {x1=x1, y1=y1, x2=x2, y2=y2, e=e or .2}
    end,
  __tostring = function (pb) return pb:tostring() end,
  __index = {
    x0 = function (pb) return pb.x1 - pb.e end,
    x3 = function (pb) return pb.x2 + pb.e end,
    y0 = function (pb) return pb.y1 - pb.e end,
    y3 = function (pb) return pb.y2 + pb.e end,
    p0 = function (pb) return v(pb.x1 - pb.e, pb.y1 - pb.e) end,
    p1 = function (pb) return v(pb.x1,        pb.y1       ) end,
    p2 = function (pb) return v(pb.x2,        pb.y2       ) end,
    p3 = function (pb) return v(pb.x2 + pb.e, pb.y2 + pb.e) end,
    tostring = function (pb)
        return pformat("LL=(%s,%s) UR=(%s,%s) e=%s",
          pb.x1, pb.y1, pb.x2, pb.y2, pb.e)
      end,
    --
    beginpicture = function (pb)
        local dimen  =  pb:p3() - pb:p0()
        local center = (pb:p3() + pb:p0()) * 0.5
        local offset =  pb:p0()
        return pformat("\\begin{picture}%s%s", dimen, offset)
      end,
    --
    grid = function (pb)
        local p = Pict2e.from {"% Grid", "% Horizontal lines:"}
        for y=pb.y1,pb.y2 do p:Line(v(pb:x0(), y), v(pb:x3(), y)) end
        p:add("% Vertical lines:")
        for x=pb.x1,pb.x2 do p:Line(v(x, pb:y0()), v(x, pb:y3())) end
        return p
      end,
    ticks = function (pb, e)
        e = e or .2
        local p = Pict2e.from {"% Ticks", "% On the vertical axis:"}
        for y=pb.y1,pb.y2 do p:Line(v(-e, y), v(e, y)) end
        p:add("% On the horizontal axis: ")
        for x=pb.x1,pb.x2 do p:Line(v(x, -e), v(x, e)) end
        return p
      end,
    axes = function (pb)
        return "% Axes"
             + Pict2e.Line(v(pb:x0(), 0), v(pb:x3(), 0))
             + Pict2e.Line(v(0, pb:y0()), v(0, pb:y3()))
      end,
    axesandticks = function (pb)
        return pb:axes() + pb:ticks()
      end,
  },
}

-- «PictBounds-tests»  (to ".PictBounds-tests")
-- (find-LATEX "edrxpict.lua" "pictp0-pictp3")
--[[
* (eepitch-lua51)
* (eepitch-kill)
* (eepitch-lua51)
dofile "2021pict2e.lua"

= PictBounds.new(v(-1,-2), v( 3, 5))
= PictBounds.new(v( 3, 5), v(-1,-2))
= PictBounds.new(v( 3, 5), v(-1,-2), 0.5)
pb = PictBounds.new(v(-1,-2), v( 3, 5))
= pb:p0()
= pb:p1()
= pb:p2()
= pb:p3()

= pb:grid()
= pb:ticks()
= pb:axes()

= pb
= pb:beginpicture()

= pb:p0()
= (pb:p0() + pb:p3())
= (pb:p0() + pb:p3()) * 0.5

--]]







--  ____            ____                  
-- |  _ \__      __/ ___| _ __   ___  ___ 
-- | |_) \ \ /\ / /\___ \| '_ \ / _ \/ __|
-- |  __/ \ V  V /  ___) | |_) |  __/ (__ 
-- |_|     \_/\_/  |____/| .__/ \___|\___|
--                       |_|              
--
-- «PwSpec»  (to ".PwSpec")
-- A "spec" for a piecewise function is something like this:
--
--   "(0,1)c--(2,3)o (2,2)c (2,1)o--(4,1)--(5,2)"
--
-- Note that its segments can be non-horizontal.
--
-- An object of this class starts with a spec, immediately calculates
-- its lists of "dxyoc"s, and from that it can generate lots of other
-- data. The list of "dxyoc"s for the spec above is:
--
--   PwSpec.from("(0,1)c--(2,3)o (2,2)c (2,1)o--(4,1)--(5,2)").dxyocs
--     = { {"dash"="" , "x"=0, "y"=1, "oc"="c"},
--         {"dash"="-", "x"=2, "y"=3, "oc"="o"},
--         {"dash"="" , "x"=2, "y"=2, "oc"="c"},
--         {"dash"="" , "x"=2, "y"=1, "oc"="o"},
--         {"dash"="-", "x"=4, "y"=1, "oc"="" },
--         {"dash"="-", "x"=5, "y"=2, "oc"="" } }
--
PwSpec = Class {
  type = "PwSpec",
  from = function (src)
      return PwSpec({src=src, dxyocs={}}):add(src)
    end,
  dxyoc_to_string = function (dxyoc)
      return format("%s(%s,%s)%s",
                    (dxyoc.dash == "-" and "--" or " "),
                    dxyoc.x, dxyoc.y, dxyoc.oc or "")
    end,
  __tostring = function (pws) return pws:tostring() end,
  __index = {
    npoints = function (pw) return #pw.points end,
    --
    -- Add points and segments.
    -- Example: pws:add("(0,1)o--(1,1)o (1,2)c (1,3)o--(2,3)c--(3,2)--(4,2)c")
    add = function (pws, str)
        local patn = "%s*([-+.%d]+)%s*"
        local pat = "(%-?)%("..patn..","..patn.."%)([oc]?)"
        for dash,x,y,oc in str:gfind(pat) do
          table.insert(pws.dxyocs, {dash=dash, x=x+0, y=y+0, oc=oc})
        end
        return pws
      end,
    --
    tostring = function (pws)
        return mapconcat(PwSpec.dxyoc_to_string, pws.dxyocs, "")
      end,
    --
    -- Express a piecewise function as a Lua function.
    conds_tbl = function (pws)
        local conds = {}
        for i=1,#pws.dxyocs do
          local P0,P1,P2 = pws.dxyocs[i], pws.dxyocs[i+1], pws.dxyocs[i+2]
          local p0,p1,p2 = P0, P1 or {}, P2 or {}
          local x0,y0,oc0       = p0.x, p0.y, p0.oc
          local x1,y1,oc1,dash1 = p1.x, p1.y, p1.oc, p1.dash
          local x2,y2,oc2,dash2 = p2.x, p2.y, p2.oc, p2.dash
          if oc0 ~= "o" then
            local cond = format("(%s == x)          and %s", x0, y0)
            table.insert(conds, cond)
          end
          if dash1 == "-" then
            local cond = format("(%s < x and x < %s)", x0, x1)
            if y1 == y0 then
              cond = format("%s and %s", cond, y0)
            else
              cond = format("%s and (%s + (x - %s)/(%s - %s) * (%s - %s))",
                             cond,   y0,       x0,  x1,  x0,    y1,  y0     )
            end
            table.insert(conds, cond)
          end
        end
        return conds
      end,
    conds = function (pws) return table.concat(pws:conds_tbl(), "  or\n") end,
    fun0 = function (pws) return "function (x) return (\n"..pws:conds().."\n) end" end,
    fun = function (pws) return expr(pws:fun0()) end,
    --
    -- Get lines and open/closed points, for drawing.
    getj = function (pws, i)
        return (pws.dxyocs[i+1]
                and (pws.dxyocs[i+1].dash == "-")
                and pws:getj(i+1)) or i
      end,
    getijs = function (pws)
        local i, j, ijs = 1, pws:getj(1), {}
        while true do
          if i < j then table.insert(ijs, {i, j}) end
          i = j + 1
          j = pws:getj(i)
          if #pws.dxyocs < i then return ijs end
        end
      end,
    getpoint = function (pws, i) return v(pws.dxyocs[i].x, pws.dxyocs[i].y) end,
    getpoints = function (pws, i, j)
        local ps = Points2.new()
        for k=i,j do ps:add(pws:getpoint(k)) end
        return ps
      end,
    topict = function (pws)
        cmds = Pict2e.new()
        for _,ij in ipairs(pws:getijs()) do
          cmds:add(pws:getpoints(ij[1], ij[2]):Line())
        end
        for i,p in ipairs(pws.dxyocs) do
          if p.oc == "o" then
            cmds:add(formatt("\\put%s{\\opendot}", pws:getpoint(i)))
          elseif p.oc == "c" then
            cmds:add(formatt("\\put%s{\\closeddot}", pws:getpoint(i)))
          end
        end
        return cmds
      end,
  },
}

-- «PwSpec-tests»  (to ".PwSpec-tests")
--[==[
* (eepitch-lua51)
* (eepitch-kill)
* (eepitch-lua51)
dofile "2022pict2e.lua"
spec = "(0,1)c--(2,3)o (2,2)c (2,1)o--(4,1)--(5,2)"
PPPV(PwSpec.from(spec).dxyocs)

spec = "(0,1)o--(1,1)o (1,4)c (1,3)o--(2,3)c--(3,2)--(4,2)c"
spec = "(0,1)c--(2,3)o (2,2)c (2,1)o--(4,1)c"
pws = PwSpec.from(spec)
= pws
= pws:conds()
= pws:fun0()
= pws:topict()
= pws:topict():bshow()
* (etv)

f = pws:fun()
= f(0.1)
= f(1.9)
= f(2)
= f(2.1)
= f(2.2)

--]==]



--  ____           _____                 _   _             
-- |  _ \__      _|  ___|   _ _ __   ___| |_(_) ___  _ __  
-- | |_) \ \ /\ / / |_ | | | | '_ \ / __| __| |/ _ \| '_ \ 
-- |  __/ \ V  V /|  _|| |_| | | | | (__| |_| | (_) | | | |
-- |_|     \_/\_/ |_|   \__,_|_| |_|\___|\__|_|\___/|_| |_|
--                                                         
-- «PwFunction»  (to ".PwFunction")
-- An object of the class PwFunction starts with a function and with
-- its sets of "important points", and it can produce lots of data
-- from that.
--
-- The most typical use of "PwFunction" is like this:
--
--   f = function (x) if x < 2 then return x^2 else return 2 end end
--   pwf = PwFunction.from(f, seq(0, 2, 0.5), 4)
--   pict = pwf:pw(1, 3)
--   pict:bshow()
--
-- We convert the function f (plus its important points) to a
-- PwFunction, and then we generate a PwSpec that draws its graph in a
-- certain interval, and we convert that PwSpec to a Pict2e object and
-- we draw it. Note that the method ":pw(...)" does two conversions:
--
--   PwFunction -> PwSpec -> Pict2e
--
PwFunction = Class {
  type = "PwFunction",
  from = function (f, ...)
      return PwFunction({f=f}):setpoints(...)
    end,
  __index = {
    --
    -- The "important points" are stored in a "Set".
    -- Example: pwf:setpoints(seq(0, 4, 0.25), 5, 6, 7)
    setpoints = function (pwf, ...)
        pwf.points = Set.new()  -- (find-angg "LUA/lua50init.lua" "Set")
        return pwf:addpoints(...)
      end,
    addpoints = function (pwf, ...)
        for _,o in ipairs({...}) do
          if     type(o) == "number" then pwf:addpoint(o)
          elseif type(o) == "table"  then pwf:addlistofpoints(o) 
          else   PP("not a point or a list of points:", o); error()
          end
        end
        return pwf
      end,
    addpoint = function (pwf, p)
        if type(p) ~= "number" then PP("Not a number:", p); error() end
        pwf.points:add(p)
        return pwf
      end,
    addlistofpoints = function (pwf, A)
        for _,p in ipairs(A) do pwf:addpoint(p) end
        return pwf
      end,
    --
    -- All important points in the interval (a,b).
    pointsin = function (pwf, a, b)
        local A = {}
        for _,x in ipairs(pwf.points:ks()) do
          if a < x and x < b then table.insert(A, x) end
        end
        return A
      end,
    --
    -- Detect discontinuities using a heuristic.
    eps = 1/32768,
    delta = 1/128,
    hasjump = function (pwf, x0, x1)
        local y0,y1 = pwf.f(x0), pwf.f(x1)
        return math.abs(y1-y0) > pwf.delta
      end,
    hasjumpl = function (pwf, x) return pwf:hasjump(x - pwf.eps, x) end,
    hasjumpr = function (pwf, x) return pwf:hasjump(x, x + pwf.eps) end,
    --
    xy = function (pwf, x, y) return pformat("(%s,%s)", x, y or pwf.f(x)) end,
    xyl = function (pwf, xc) return pwf:xy(xc - pwf.eps) end,
    xyr = function (pwf, xc) return pwf:xy(xc + pwf.eps) end,
    jumps_and_xys = function (pwf, xc)
        local xyl, xyc, xyr = pwf:xyl(xc), pwf:xy(xc), pwf:xyr(xc)
        local jumpl = pwf:hasjumpl(xc) and "l" or ""
        local jumpr = pwf:hasjumpr(xc) and "r" or ""
        local jumps = jumpl..jumpr      -- "lr", "l", "r", or ""
        return jumps, xyl, xyc, xyr
      end,
    --
    xym = function (pwf, xc)
        local jumps, xyl, xyc, xyr = pwf:jumps_and_xys(xc)
        if     jumps == ""   then str = xyc
        elseif jumps == "l"  then str = format("%so %sc",     xyl, xyc)
        elseif jumps == "r"  then str = format(    "%sc %so",      xyc, xyr)
        elseif jumps == "lr" then str = format("%so %sc %so", xyl, xyc, xyr)
        end
        return str
      end,
    --
    -- pwf:pwspec(a, b) converts a pwf to a "piecewise spec".
    -- This is tricky, so let's see an example. Suppose that:
    --
    --   pwf:pointsin(a, b) = {c}.
    --
    -- Then in the simplest case pwf:piecewise(a, b) would generate a
    -- piecewise spec like this,
    --
    --   "(a,f(a))--(c,f(c))--(b,f(b))"
    --
    -- but we have tricks for dealing with discontinuites and drawing
    -- closed dots and open dots... for example, we can generate
    -- something like this:
    --
    --   "(a,f(a))--(c-eps,f(c-eps))o (c,f(c))c--(b,f(b))"
    --
    -- The hard work to convert c to 
    --
    --             "(c-eps,f(c-eps))o (c,f(c))c"
    --
    -- is done by the function xym, defined above; the case "_o _c"
    -- corresponds to jumps == "l". Note that the "eps"s usually
    -- disappear when each coordinate of the "(x,y)"s is rounded to at
    -- most three decimals.
    --
    -- Btw, I think that the extremities are "(a+eps,f(a+eps))" and
    -- "(b-eps,f(b+eps))", not "(a,f(a))" and "(b,f(b))"... TODO:
    -- check that.
    --
    -- Btw 2: pwf:pwspec(...) can generate other kinds of output if
    -- it is called with the right hackish optional arguments.
    --
    pwspec = function (pwf, a, b, method, sep)
        local method = method or "xym"
        local sep = sep or "--"
        local xym = function (x) return pwf[method](pwf, x) end
        local str
	--
	-- Add points (including discontinuities) and "--"s to str.
        str = pwf:xyr(a)
        for _,x in ipairs(pwf:pointsin(a, b)) do str = str..sep..xym(x) end
        str = str..sep..pwf:xyl(b)
        return str
      end,
    --
    -- :pw(a,b) generates a Pict2e object that
    -- draws f in the interval [a,b].
    pw = function (pwf, a, b)
        return PwSpec.from(pwf:pwspec(a, b)):topict()
      end,
    --
    --
    --
    -- Methods that call pwspec in hackish ways.
    -- TODO: fix names, write tests.
    --
    -- :lineify(...) only works when there are no discontinuities.
    --
    lineify = function (pwi, a, b)
        return "\\Line"..pwi:piecewise(a, b, nil, "")
      end,
    --
    --  ____  _                                                            
    -- |  _ \(_) ___ _ __ ___   __ _ _ __  _ __    ___ _   _ _ __ ___  ___ 
    -- | |_) | |/ _ \ '_ ` _ \ / _` | '_ \| '_ \  / __| | | | '_ ` _ \/ __|
    -- |  _ <| |  __/ | | | | | (_| | | | | | | | \__ \ |_| | | | | | \__ \
    -- |_| \_\_|\___|_| |_| |_|\__,_|_| |_|_| |_| |___/\__,_|_| |_| |_|___/
    --                                                                     
    -- «PwFunction-Riemann»  (to ".PwFunction-Riemann")
    -- The methods below are used to draw several kinds (or "methods")
    -- of Riemann sums as rectangles touching the x axis, and to draw
    -- the differences between two methods as rectangles "floating in
    -- the air".
    -- See: (find-LATEX "2021pict2e.lua" "Piecewisify")
    --      (find-LATEX "2021pict2e.lua" "Piecewisify" "pol1")
    --
    piecewise_pol1 = function (pwi, xc) -- for polygons
        local jumps, xyl, xyc, xyr = pwi:jumps_and_xys(xc)
        if jumps == ""  
        then return xyc
        else return format("%s%s", xyl, xyr)
        end
      end,
    piecewise_pol = function (pwi, a, b)
        local a0, b0 = pwi:xy(a, 0), pwi:xy(b, 0)
        return a0..pwi:piecewise(a, b, "piecewise_pol1", "")..b0
      end,
    pol = function (pwi, a, b, star)
        -- return "\\polygon"..(star or "")..pwi:piecewise_pol(a, b)
        return Pict2e.from("\\polygon"..(star or "")..pwi:piecewise_pol(a, b))
      end,
    --
    inforsup = function (pwi, maxormin, a, b)
        local y = pwi.f(a)
        local consider = function (x) y = maxormin(y, pwi.f(x)) end
        consider(a + pwi.eps)
        for _,x in ipairs(pwi:pointsin(a, b)) do
          consider(x - pwi.eps)
          consider(x)
          consider(x + pwi.eps)
        end
        consider(b - pwi.eps)
        consider(b)
        return y
      end,
    inf = function (pwi, a, b) return pwi:inforsup(min, a, b) end,
    sup = function (pwi, a, b) return pwi:inforsup(max, a, b) end,
    max = function (pwi, a, b) return max(pwi.f(a), pwi.f(b)) end,
    min = function (pwi, a, b) return min(pwi.f(a), pwi.f(b)) end,
    zero = function (pwi) return 0 end,
    method = function (pwi, mname, a, b) return pwi[mname](pwi, a, b) end,
    rct = function (pwi, mname1, mname2, a, b)
        local y1 = pwi:method(mname1, a, b)
        local y2 = pwi:method(mname2 or "zero", a, b)
        return pformat("(%s,%s)(%s,%s)(%s,%s)(%s,%s)", a,y1, a,y2, b,y2, b,y1)
      end,
    rects = function (pwi, ptn, mname1, mname2, star)
        local p = Pict2e.new()
        for i=1,ptn:N() do
          local ai,bi = ptn:ai(i), ptn:bi(i)
          local rct = pwi:rct(mname1, mname2, ai, bi)
          p:add(format("\\polygon%s%s\n", star or "*", rct))
        end
        return p
      end,
  },
}

-- «PwFunction-tests»  (to ".PwFunction-tests")
--[[
* (eepitch-lua51)
* (eepitch-kill)
* (eepitch-lua51)
dofile "2022pict2e.lua"
f = function (x) if x < 2 then return x^2 else return 2 end end
pwf  = PwFunction.from(f, seq(0, 2, 0.5), 4)
PPPV(pwf)
spec = pwf:pwspec(0, 4)
PPPV(spec)
spec = pwf:pwspec(1, 3)
PPPV(spec)
pws = PwSpec.from(spec)
PPPV(pws)
pict = pws:topict()
PPPV(pict)
= pict

f = function (x) if x < 2 then return x^2 else return 2 end end
pwf  = PwFunction.from(f, seq(0, 2, 0.5), 4)
= pwf:pwspec(0, 4)
= pwf:pw    (0, 4)
= pwf:pwspec(1, 3)
= pwf:pw    (1, 3)
= pwf:pw    (1, 3):bshow()
* (etv)
= pwf:pw    (0, 4):bshow()
* (etv)

--]]




--  _____                 
-- | ____|_  ___ __  _ __ 
-- |  _| \ \/ / '_ \| '__|
-- | |___ >  <| |_) | |   
-- |_____/_/\_\ .__/|_|   
--            |_|         
--
-- «Expr»  (to ".Expr")

Expr = Class {
  type     = "Expr",
  __tostring = function (expr) return expr:tostring() end,
  __index = {
    expandskel1 = function (expr, str)
        local key = tonumber(str) or str
        return expr[key]
      end,
    expandskel = function (expr, skel)
        local expand1 = function (akey)
            local strkey = akey:sub(2,-2)
            local key = tonumber(strkey) or strkey
            PP(akey, strkey, key)
            return expr[key]
          end
        return (skel:gsub("%b<>", expand1))
      end,
    tostring = function (expr)
        return expr:expandskel(expr.skel)
      end,
  },
}


-- «Expr-tests»  (to ".Expr-tests")
--[==[
* (eepitch-lua51)
* (eepitch-kill)
* (eepitch-lua51)
dofile "2022pict2e.lua"


PP(tonumber("aaa"))
PP(tonumber("123") or "123")

skel = [[∫<1>\,d<v>]]
= skel
PP(skel:match("%b<>"))
PP(skel:match("%b<>"):sub(2,-2))

e = Expr {skel = [[∫<1>\,d<v>]], v="x", [1]="foo"}
= e
= e:expandskel1("1")
= e:expandskel1("v")
PPPV(e)



--]==]




-- (c3m212dnp 9 "point-of-view")
-- (c3m212dna   "point-of-view")
-- (find-LATEX "2021-1-C3-3D.lua")
-- V3.__index.tostring = function (v) return v:v2string() end


-- __     _______ 
-- \ \   / /___ / 
--  \ \ / /  |_ \ 
--   \ V /  ___) |
--    \_/  |____/ 
--                
-- «V3»  (to ".V3")
-- (find-es "dednat" "V3")
--
V3 = Class {
  type       = "V3",
  --
  output     = "3D",
  output2D   = function () V3.output = "2D" end,
  output3D   = function () V3.output = "3D" end,
  p1 = V{2,-1},
  p2 = V{2,1},
  p3 = V{0,2},
  --
  __tostring = function (v) return v:tostring() end,
  __add      = function (v, w) return V3{v[1]+w[1], v[2]+w[2], v[3]+w[3]} end,
  __sub      = function (v, w) return V3{v[1]-w[1], v[2]-w[2], v[3]-w[3]} end,
  __unm      = function (v) return v*-1 end,
  __mul      = function (v, w)
      local ktimesv   = function (k, v) return V3{k*v[1], k*v[2], k*v[3]} end
      local innerprod = function (v, w) return v[1]*w[1] + v[2]*w[2] + v[3]*w[3] end
      if     type(v) == "number" and type(w) == "table" then return ktimesv(v, w)
      elseif type(v) == "table" and type(w) == "number" then return ktimesv(w, v)
      elseif type(v) == "table" and type(w) == "table"  then return innerprod(v, w)
      else error("Can't multiply "..tostring(v).."*"..tostring(w))
      end
    end,
  __index = {
    tostring = function (v)
        if V3.output == "3D"
        then return v:v3string()
	else return v:v2string()
        end
      end,
    v3string = function (v) return pformat("(%s,%s,%s)", v[1], v[2], v[3]) end,
    v2string = function (v) return tostring(v:tov2()) end,
    --
    -- Convert v3 to v2 using a primitive kind of perspective.
    -- Adjust p1, p2, p3 to change the perspective.
    tov2 = function (v) return v[1]*V3.p1 + v[2]*V3.p2 + v[3]*V3.p3 end,
    --
    Line = function (A, v) return pformat("\\Line%s%s", A, A+v) end,
    -- Lines = function (A, v, w, i, j)
    --     local bprint, out = makebprint()
    --     for k=i,j do bprint((A+k*w):Line(v)) end
    --     return out()
    --   end,
    Lines = function (A, v, w, i, j)
        local out = Pict2e.new()
        for k=i,j do out = out + (A+k*w):Line(v) end
        return out
      end,
    --
    xticks = function (o,n,eps)
        eps = eps or 0.15
        return v3(0,-eps,0):Lines(v3(0,2*eps,0), v3(1,0,0), 0, n)
      end,
    yticks = function (o,n,eps)
        eps = eps or 0.15
        return v3(-eps,0,0):Lines(v3(2*eps,0,0), v3(0,1,0), 0, n)
      end,
    zticks = function (o,n,eps)
        eps = eps or 0.15
        return v3(-eps,0,0):Lines(v3(2*eps,0,0), v3(0,0,1), 0, n)
      end,
    axeswithticks = function (o,x,y,z)
        out = Pict2e.new()
        out = out + v3(0,0,0):Line(v3(x+0.5, 0, 0))
        out = out + v3(0,0,0):Line(v3(0, y+0.5, 0))
        out = out + v3(0,0,0):Line(v3(0, 0, z+0.5))
        out = out + o:xticks(x)
        out = out + o:yticks(y)
        out = out + o:zticks(z)
        return out
      end,
    xygrid = function (o,x,y)
        out = Pict2e.new()
        out = out + v3(0,0,0):Lines(v3(0,y,0), v3(1,0,0), 0, x)
        out = out + v3(0,0,0):Lines(v3(x,0,0), v3(0,1,0), 0, y)
        return out
      end,
    gridandticks = function (o,x,y,z)
        return o:xygrid(x,y):color("gray")
             + o:axeswithticks(x,y,z)
      end,
  },
}

v3 = function (x,y,z) return V3{x,y,z} end

-- Old way:
-- V3.__index.tostring = function (v) return v:v2string() end
-- V3.__index.tostring = function (v) return v:v3string() end

-- «V3-tests»  (to ".V3-tests")
--[[
* (eepitch-lua51)
* (eepitch-kill)
* (eepitch-lua51)
dofile "2022pict2e.lua"

o = v3(2,3,4)
= o
= o:v3string()
= o:v2string()

V3.output2D(); print(o)
V3.output3D(); print(o)
V3.output3D(); print(o:xticks(4, 0.2))
V3.output3D(); print(o:axeswithticks(2, 3, 4))
V3.output2D(); print(o:axeswithticks(2, 3, 4))
V3.output2D(); print(o:xygrid(2, 3))
V3.output2D(); print(o:xygrid(2, 3):color("gray"))
V3.output2D(); print(o:axeswithticks(2, 3, 4):bshow())
* (etv)
Pict2e.sw = v(-1,-3); Pict2e.ne = v(7,9)
V3.output2D(); print(o:axeswithticks(2, 3, 4):bshow())
* (etv)
V3.output2D(); print(o:axeswithticks(2, 3, 4):b0show())
* (etv)
V3.output2D(); print((o:xygrid(2, 3):color("gray") +
                      o:axeswithticks(2, 3, 4)
                     ):b0show())
* (etv)
V3.output2D(); print(o:gridandticks(2, 3, 4):b0show())
* (etv)

* (eepitch-lua51)
* (eepitch-kill)
* (eepitch-lua51)
dofile "2022pict2e.lua"
Pict2e.sw = v(-1,-3); Pict2e.ne = v(7,9)
V3.output2D();

gt = v3(0,0,0):gridandticks(2, 3, 4)
pu = Pict2e.new()
pu = Pict2e.new():Unitlength("2.5pt")
pu = Pict2e.unitlength("2.5pt")
= pu
= gt
= pu + gt
V3.output2D(); print((pu + gt):b0show())
* (etv)

--]]




--  ____              __                
-- / ___| _   _ _ __ / _| __ _  ___ ___ 
-- \___ \| | | | '__| |_ / _` |/ __/ _ \
--  ___) | |_| | |  |  _| (_| | (_|  __/
-- |____/ \__,_|_|  |_|  \__,_|\___\___|
--                                      
-- «Surface0»  (to ".Surface0")
-- (find-dn6 "picture.lua" "V")
-- (find-dn6 "diagforth.lua" "newnode:at:")
--
Surface0 = Class {
  type = "Surface0",
  new  = function (f, x0, y0)
      return Surface0 {f=f, x0=x0, y0=y0, xy0=v(x0, y0),
                      p=Pict2e.bounds()}
    end,
  __index = {
    xyz = function (s, xy, zvalue)
        return v3(xy[1], xy[2], zvalue or s.f(xy[1], xy[2]))
      end,
    xyztow = function (s, xy1, xy2, zvalue, k)
        return s:xyz(tow(xy1, xy2, k), zvalue)
      end,
    segment = function (s, xy1, xy2, zvalue, n)
        local pts = Points2.new()
        for i=0,n do pts:add(s:xyztow(xy1, xy2, zvalue, i/n)) end
        return pts:Line()
      end,
    pillar = function (s, xy)
        return Points2 {s:xyz(xy, 0), s:xyz(xy, nil)} :Line()
      end,
    pillars = function (s, xy1, xy2, n)
        local str = Pict2e.new()
        for i=0,n do str = str + s:pillar(tow(xy1, xy2, i/n)) end
        return str
      end,
    segmentstuff = function (s, xy1, xy2, n, what)
        local str = Pict2e.new()
        if what:match"0" then str = str + s:segment(xy1, xy2, 0,   1) end
        if what:match"c" then str = str + s:segment(xy1, xy2, nil, n) end
        if what:match"p" then str = str + s:pillars(xy1, xy2,      n) end
        return str
      end,
    --
    stoxy = function (s, str)
        expr(format("v(%s)", str))
      end,
    squarestuff = function (s, dxy0s, dxy1s, n, what)
        local dxy0 = expr(format("v(%s)", dxy0s))
        local dxy1 = expr(format("v(%s)", dxy1s))
        local xy1 = s.xy0 + dxy0
        local xy2 = s.xy0 + dxy1
        return s:segmentstuff(xy1, xy2, n, what)
      end,
    squarestuffp = function (s, n, what, pair)
        local dxy0,dxy1 = unpack(split(pair))
	return s:squarestuff(dxy0, dxy1, n, what)
      end,
    squarestuffps = function (s, n, what, listofpairs)
        local str = Pict2e.new()
        for _,pair in ipairs(listofpairs) do
          str = str + s:squarestuffp(n, what, pair)
        end
        return str
      end,
    --
    horizontals = function (s, n, what)
        return s:squarestuffps(n, what, {
          "-1,-1 1,-1", "-1,0 1,0", "-1,1 1,1"
          })
      end,
    verticals = function (s, n, what)
        return s:squarestuffps(n, what, {
          "-1,-1 -1,1", "0,-1 0,1", "1,-1 1,1"
          })
      end,
    diagonals = function (s, n, what)
        return s:squarestuffps(n, what, {
          "-1,-1 1,1", "-1,1 1,-1"
          })
      end,
    square = function (s, n, what)
        return s:horizontals(n, what)
             + s:verticals  (n, what)
      end,
  },
}

-- «Surface0-tests»  (to ".Surface0-tests")
-- (defun st () (interactive) (find-LATEXfile "2022pict2e.lua" "Surface0-tests"))
--
--[[
* (eepitch-lua51)
* (eepitch-kill)
* (eepitch-lua51)
dofile "2022pict2e.lua"
V3.output2D();
Pict2e.sw = v(-1,-3); Pict2e.ne = v(7,9)
pu = Pict2e.unitlength("10pt")
gt = v3():gridandticks(5, 4, 3)

= pu + gt
V3.output2D(); print((pu + gt):b0show())
= Show.log
* (etv)

fun = function (x, y) return (x-4)^2+(y-3)^2 end
srf = Surface0.new(fun, 4, 3)
fig = srf:segment(v(3,2), v(5,2), 0,   2)
fig = srf:segment(v(3,2), v(5,2), nil, 16)
fig = srf:segmentstuff(v(3,2), v(5,2), 16, "0cp")
print((pu + gt + fig):b0show())
* (etv)

= srf:xyz(v(3, 2))
= srf:xyz(v(3, 2), 0)
= srf:xyz(v(3, 3))
= srf:xyz(v(3, 3), 0)




* (eepitch-lua51)
* (eepitch-kill)
* (eepitch-lua51)
dofile "2022pict2e.lua"

F = function (x, y) return 10*x + y end
F = function (x, y) return math.sin(x) * math.sin(y) + 2 end
srf = Surface0.new(F, 3, 4)
= srf:xyz(v(2, 5))
= srf:xyz(v(2, 5), 0)

= srf:xyztow(v(2,5), v(22,25), nil,  0.5)
= srf:xyztow(v(2,5), v(22,25), 0,    0.5)
= srf:segment(v(2,5), v(22,25), 0,   2)
= srf:segment(v(2,5), v(22,25), nil, 2)
= srf:pillar(v(2,5))
= srf:segmentstuff(v(2,5), v(22,25), 2, "0cp")

V3.output2D()
= srf:segmentstuff(v(2,5), v(22,25), 2, "0cp")
= srf:segmentstuff(v(2,5), v(22,25), 2, "0cp"):show()
V3.output3D()
* (etv)

= srf:squarestuff("0,0", "2,2", 2, "0")
= srf:squarestuff("0,0", "2,2", 2, "c")
= srf:squarestuff("0,0", "2,2", 2, "p")
= srf:squarestuffp(             2, "p", "0,0 2,2")

= srf:square   (2, "p")
= srf:square   (4, "p")
= srf:square   (2, "c")
= srf:square   (4, "c")
= srf:square   (8, "c")
= srf:diagonals(2, "p")

fw = function (x) return max(min(x-2, 6-x), 0) end
FP = function (x,y) return min(fw(x), fw(y)) end
FC = function (x,y) return max(fw(x), fw(y)) end
sP = Surface0.new(FP, 4, 4)
= sP:segment(v(0,4), v(6,4), nil, 6)

-- Pict2e.sw = v();
-- Pict2e.ne = v();


-- ^ Used by:
-- (c3m211cnp 15 "figura-piramide")
-- (c3m211cna    "figura-piramide")
-- (c3m211cnp 16 "cruz")
-- (c3m211cna    "cruz")

--]]




--  ____              __                
-- / ___| _   _ _ __ / _| __ _  ___ ___ 
-- \___ \| | | | '__| |_ / _` |/ __/ _ \
--  ___) | |_| | |  |  _| (_| | (_|  __/
-- |____/ \__,_|_|  |_|  \__,_|\___\___|
--                                      
-- «Surface»  (to ".Surface")

Surface_fmt = [[
Pict2e:  <unitlength:nv> <sw:nv> <ne:nv>
V3:      <p1:nv> <p2:nv> <p3:nv>
Surface: <maxx:nv> <maxy:nv> <maxz:nv>
         <x0:nv> <y0:nv>
         <f:nv>
         <xya:nv> <xyb:nv> <nsteps:nv>
]]

Surface = Class {
  type = "Surface",
  new  = function (A) return Surface(copy(A)) end,
  __tostring = function (s2) return s2:tostring() end,
  __index = {
    mod = function (df, A)   -- returns a modified copy
        df = copy(df)
        for key,val in pairs(A) do df[key] = val end
        return Surface(df)
      end,
    --
    fmt = function () return Surface_fmt end,
    --
    tostring = function (df, fmt)
        local angtostring = function (name, how)
	    if how == "nv"  then return df:nv(name) end
	    if how == "n2D" then return df:nmethod("v3string", name) end
	    if how == "n3D" then return df:nmethod("v2string", name) end
            return format("[%s by %s]", name, how)
          end
        local pat = "<([!-~]-):([!-~]-)>"
        local out = (fmt or df:fmt()):gsub(pat, angtostring)
        return out
      end,
    nv = function (df, name)
        return format("%s=%s", name, tostring(df[name]))
      end,
    nmethod = function (df, methodname, name)
        local o = df[name]
        local f = (type(o) == "table") and o[methodname]
        return format("%s=%s", name, (f and f(o)) or tostring(o))
      end,
    --
    setbounds = function (s2)
        Pict2e.sw = s2.sw
        Pict2e.ne = s2.ne
      end,
    setperspective = function (s2)
        V3.p1 = s2.p1
        V3.p2 = s2.p2
        V3.p3 = s2.p3
      end,
    pu = function (s2) return Pict2e.unitlength(s2.unitlength) end,
    gt = function (s2) return v3():gridandticks(s2.maxx, s2.maxy, s2.maxz) end,
    xy0 = function (s2) return v(s2.x0, s2.y0) end,
    xyz = function (s2, xy, zspec)
        local x,y = xy[1], xy[2]
        local z = (type(zspec) == "number") and zspec or s2.f(x,y)
        return v3(x,y,z)
      end,
    xyab = function (s2, alpha) return tow(s2.xya, s2.xyb, alpha) end,
    xyzab = function (s2, alpha, zspec) return s2:xyz(s2:xyab(alpha), zspec) end,
    curve = function (s2, zspec)
        local pts = Points2.new()
        for k=0,s2.nsteps do
          local alpha = k/s2.nsteps
          pts:add(s2:xyzab(alpha, zspec))
        end
        return pts:Line()
      end,
    curverelative = function (s2, zspec, Dxya, Dxyb)
        local s2alt = s2:mod {xya=s2:xy0()+Dxya, xyb=s2:xy0()+Dxyb}
        return s2alt:curve(zspec)
      end,
    --
    sixcurves = function (s2, zspec)
        local out = Pict2e.new()
        out = out + s2:curverelative(zspec, v(-1,-1), v(1,-1))
        out = out + s2:curverelative(zspec, v(-1, 0), v(1, 0))
        out = out + s2:curverelative(zspec, v(-1, 1), v(1, 1))
        out = out + s2:curverelative(zspec, v(-1,-1), v(-1,1))
        out = out + s2:curverelative(zspec, v( 0,-1), v( 0,1))
        out = out + s2:curverelative(zspec, v( 1,-1), v( 1,1))
        return out
      end,
    pillar = function (s2, xy) return Points2({s2:xyz(xy,0), s2:xyz(xy,nil)}):Line() end,
    pillars0 = function (s2, xa, xb, xstep, ya, yb, ystep)
        local out = Pict2e.new()
        for y=ya,yb,ystep do
          for x=xa,xb,xstep do
            out = out + s2:pillar(v(x,y))
          end
        end
        return out
      end,
    pillars = function (s2)
        return s2:pillars0(s2.x0-1, s2.x0+1, 1, s2.y0-1, s2.y0+1, 1)
      end,
    --
    base = function (s2)
        s2:setbounds()
        s2:setperspective()
        V3:output2D()
        return s2:pu() + s2:gt()
      end,
    twoDgrid = function (s2)
        s2:setbounds()
        return Pict2e.bounds():grid():axesandticks()
      end,
    fig = function (s2, what)
        what = what or "0pc"
        local out = Pict2e.new()
        if what:match"0" then out = out + s2:sixcurves(0)   end
        if what:match"p" then out = out + s2:pillars(0)     end
        if what:match"c" then out = out + s2:sixcurves(nil) end
        return out
      end,
  },
}

-- «Surface-tests»  (to ".Surface-tests")
--[[
* (eepitch-lua51)
* (eepitch-kill)
* (eepitch-lua51)
dofile "2022pict2e.lua"
s2 = Surface {
     unitlength="10pt", sw=v(-1,-3), ne=v(7,9),
     p1=v(2,-0.5), p2=v(0.5,1.5), p3=v(0,0.5),
     maxx=5, maxy=4, maxz=3,
     x0=4, y0=3, nsteps=16,
     -- f = function (x, y) return (x-4)^2 + (y-3)^2 end,
     f = function (x, y) return (x-3) + (y-2) end,
  }
s2b = s2:mod {xya=v(1,2), xyb=v(3,4)}
= s2b
= s2

print((                s2:base() + s2:fig()):b0show())
print((s2:twoDgrid() + s2:base() + s2:fig()):b0show())
* (etv)
= Show.log

--]]





-- Local Variables:
-- coding:  utf-8-unix
-- ee-tla: "p22"
-- End: