anch-snc.lmt /size: 7286 b    last modification: 2021-10-28 13:50
1if not modules then modules = { } end modules ['anch-snc'] = {
2    version   = 1.001,
3    comment   = "companion to anch-snc.mkiv",
4    author    = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
5    copyright = "PRAGMA ADE / ConTeXt Development Team",
6    license   = "see context related readme files"
7}
8
9local tonumber, next, setmetatable, rawget = tonumber, next, setmetatable, rawget
10local concat, sort, remove, copy = table.concat, table.sort, table.remove, table.copy
11local match, find = string.match, string.find
12local lpegmatch, lpegpatterns = lpeg.match, lpeg.patterns
13local setmetatableindex = table.setmetatableindex
14local P, Cc = lpeg.P, lpeg.Cc
15
16graphics                = graphics or { }
17local synchronizers     = { }
18graphics.synchronizers  = synchronizers
19
20local jobpositions      = job.positions
21
22local p_number = lpegpatterns.cardinal/tonumber
23local p_space  = lpegpatterns.whitespace^0
24local p_option = p_number * ((P(",") * p_space * P("reset") * Cc(true)) + Cc(false)) -- for now
25
26local list     = { }
27local kinds    = {
28    above    = 1,
29    continue = 2,
30    nothing  = 3,
31    normal   = 4,
32    below    = 5,
33}
34
35local lastdone = { }
36
37function synchronizers.collect(category,realpage,region)
38    local all = jobpositions.getsync(category)
39    local m   = 0
40    local n   = #all
41    list = { }
42    if region and region ~= "" then
43        -- successive can be optimized when we sort by region
44        local start = 1
45        local done  = false
46        local last, rtop, rbot
47        for i=start,n do
48            local pos = all[i]
49            local p = pos.p
50            local r = pos.r
51            if r == region then
52                if not done then
53                    local region = jobpositions.collected[r]
54                    list.region  = region
55                    list.page    = region
56                    rtop = (region.y or 0) + (region.h or 0)
57                    rbot = (region.y or 0) - (region.d or 0)
58                    last = { kind = "nothing", top = rtop, bottom = 0, task = 0 }
59                    m = m + 1 ; list[m] = last
60                    done = true
61                end
62                local top = pos.y + pos.h
63                last.bottom = top
64                local task, reset = lpegmatch(p_option,pos.e)
65                last = { kind = "normal", top = top, bottom = 0, task = task }
66                m = m + 1 ; list[m] = last
67            end
68        end
69        if done then
70            last.bottom = rbot
71        end
72    else
73        local start = all.start or 1
74        local done  = false
75        local last, rtop, rbot, ptop, pbot
76        for i=start,n do
77            local pos = all[i]
78            local p = pos.p
79            if p == realpage then
80                if not done then
81                    local region = jobpositions.collected[pos.r]
82                    local page   = jobpositions.collected["page:"..realpage] or region
83                    list.region  = region
84                    list.page    = page
85                    rtop = (region.y or 0) + (region.h or 0)
86                    rbot = (region.y or 0) - (region.d or 0)
87                    ptop = (page  .y or 0) + (page  .h or 0)
88                    pbot = (page  .y or 0) - (page  .d or 0)
89                    last = { kind = "above", top = ptop, bottom = rtop, task = 0 }
90                    m = m + 1 ; list[m] = last
91                    if i > 1 then
92                        local task, reset = lpegmatch(p_option,all[i-1].e)
93                        last = { kind = "continue", top = rtop, bottom = 0, task = task }
94                        m = m + 1 ; list[m] = last
95                    else
96                        last = { kind = "nothing", top = rtop, bottom = 0, task = 0 }
97                        m = m + 1 ; list[m] = last
98                    end
99                    done = true
100                end
101                local top = pos.y + pos.h
102                last.bottom = top
103                local task, reset = lpegmatch(p_option,pos.e)
104                if reset then
105                    local l = list[2]
106                    l.kind = "nothing"
107                    l.task = 0
108                end
109                last = { kind = "normal", top = top, bottom = 0, task = task }
110                m = m + 1 ; list[m] = last
111            elseif p > realpage then
112                all.start = i -- tricky, only for page
113                break
114            end
115        end
116        if done then
117            last.bottom = rbot
118            last = { kind = "below", top = rbot, bottom = pbot, task = 0 }
119            m = m + 1 ; list[m] = last
120            lastdone[category] = {
121                { kind = "above", top = ptop, bottom = rtop, task = 0 },
122                { kind = "continue", top = rtop, bottom = rbot, task = list[#list-1].task }, -- lasttask
123                { kind = "below", top = rbot, bottom = pbot, task = 0 },
124                region = list.region,
125                page   = list.page,
126            }
127        else
128            local l = lastdone[category]
129            if l then
130                list = copy(l) -- inefficient, maybe metatable for region/page
131                m    = 3
132            end
133        end
134    end
135    return m
136end
137
138function synchronizers.extend()
139     local n = #list
140     if n > 0 then
141        for i=1,n do
142            local l = list[i]
143            local k = l.kind
144            if k == "nothing" then
145                local ll = list[i+1]
146                if ll and ll.kind == "normal" then
147                    ll.top = l.top
148                    remove(list,i)
149                    n = #list
150                    break
151                end
152            end
153        end
154    end
155    return n
156end
157
158function synchronizers.prune()
159     local n = #list
160     if n > 0 then
161        if list[1].kind == "above" then
162            remove(list,1)
163        end
164        if list[1].kind == "nothing" then
165            remove(list,1)
166        end
167        if list[#list].kind == "below" then
168            remove(list,#list)
169        end
170        n = #list
171    end
172    return n
173end
174
175function synchronizers.collapse()
176    local n = #list
177    if n > 0 then
178        local m = 0
179        local p = nil
180        for i=1,n do
181            local l = list[i]
182            local t = l.task
183            if p == t then
184                list[m].bottom = l.bottom
185            else
186                m = m + 1
187                list[m] = l
188            end
189            p = t
190        end
191        for i=n,m+1,-1 do
192            list[i] = nil
193        end
194        n = m
195    end
196    return n
197end
198
199-- These operate on the currently set list:
200
201function synchronizers.getsize  ()  return #list               end
202function synchronizers.gettop   (n) return list[n].top         end
203function synchronizers.getbottom(n) return list[n].bottom      end
204function synchronizers.getkind  (n) return kinds[list[n].kind] end
205function synchronizers.gettask  (n) return list[n].task        end
206
207function synchronizers.getx() return list.page.x or 0 end
208function synchronizers.gety() return list.page.y or 0 end
209function synchronizers.getw() return list.page.w or 0 end
210function synchronizers.geth() return list.page.h or 0 end
211function synchronizers.getd() return list.page.d or 0 end
212
213-- function mp.xxOverlayRegion()
214--     local r = tokens.getters.macro("m_overlay_region")
215--     mp.quoted('"'.. r .. '"')
216-- end
217
218