1if not modules then modules = { } end modules ['anch-loc'] = {
2 version = 1.001,
3 comment = "companion to anch-loc.lmtx",
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 next, type = next, type
10local setmetatableindex, sortedhash, insert, remove = table.setmetatableindex, table.sortedhash, table.insert, table.remove
11
12local context = context
13
14local nuts = nodes.nuts
15local nodepool = nodes.pool
16local whatever = nodepool.userids["localanchor"]
17local new_usernode = nodepool.usernode
18local new_kern = nuts.pool.kern
19local getbox = nuts.getbox
20local getwidth = nuts.getwidth
21local setwidth = nuts.setwidth
22local getprop = nuts.getprop
23local insertbefore = nuts.insertbefore
24local insertafter = nuts.insertafter
25local setattributelist = nuts.setattributelist
26
27local texgetbox = tex.getbox
28
29local implement = interfaces.implement
30
31local analyze = drivers.converters.analyze
32
33local dimension_value = tokens.values.dimension
34
35local v_left = interfaces.variables.left
36local v_middle = interfaces.variables.middle
37
38local positionsstack = setmetatableindex("table")
39
40local function allocate2(t,k)
41 local v = { min = false, max = false }
42 t[k] = v
43 return v
44end
45
46local function allocate1(t,k)
47 local v = setmetatableindex({ cnt = { }, min = false, max = false }, allocate2)
48 t[k] = v
49 return v
50end
51
52local positions = setmetatableindex(allocate1)
53
54
55
56local function pushpositions()
57 insert(positionsstack,positions)
58 positions = setmetatableindex(allocate1)
59end
60
61local function poppositions()
62 positions = remove(positionsstack) or { }
63end
64
65local function initializepositions(driver,specification)
66
67
68end
69
70
71
72
73local function collectpositions(current,pos_h,pos_v,cur_b)
74
75
76 local data = getprop(current,"data")
77 local hash = positions[data.name]
78 local x = data.x
79 local y = data.y
80 if not hash.min then
81 hash.min = x
82 hash.max = x
83 elseif x > hash.max then
84 hash.max = x
85 end
86 hash = hash[x]
87 if not hash.min then
88 hash.min = y
89 hash.max = y
90 elseif y > hash.max then
91 hash.max = y
92 end
93 hash[y] = { pos_h, pos_v, data, current, 0, false, cur_b }
94end
95
96local function valid(name,x,y)
97 if positions then
98 local xlist = positions[name]
99 if xlist then
100 xlist = xlist[x]
101 return xlist and xlist[y]
102 end
103 end
104end
105
106local function anchorx(name,x,y)
107 local v = valid(name,x,y)
108 return v and v[1] or 0
109end
110
111local function anchory(name,x,y)
112 local v = valid(name,x,y)
113 return v and v[2] or 0
114end
115
116local function anchorht(name,x,y)
117 local v = valid(name,x,y)
118 if v then
119 return v[7][2]
120 else
121 return 0
122 end
123end
124
125local function anchordp(name,x,y)
126 local v = valid(name,x,y)
127 if v then
128 return v[7][3]
129 else
130 return 0
131 end
132end
133
134local function anchorlr(name,x,y)
135 local v = valid(name,x,y)
136 if v then
137 return v[1] + v[7][1], v[2] - v[7][3]
138 else
139 return 0, 0
140 end
141end
142
143local function anchorur(name,x,y)
144 local v = valid(name,x,y)
145 if v then
146 return v[1] + v[7][1], v[2] + v[7][2]
147 else
148 return 0, 0
149 end
150end
151
152local function anchorul(name,x,y)
153 local v = valid(name,x,y)
154 if v then
155 return v[1], v[2] + v[7][2]
156 else
157 return 0, 0
158 end
159end
160
161local function anchorll(name,x,y)
162 local v = valid(name,x,y)
163 if v then
164 return v[1], v[2] - v[7][3]
165 else
166 return 0, 0
167 end
168end
169
170local function anchorxy(name,x,y)
171 local v = valid(name,x,y)
172 if v then
173 return v[1], v[2]
174 else
175 return 0, 0
176 end
177end
178
179local driver = {
180 actions = {
181 initialize = initializepositions,
182
183 },
184 flushers = {
185 userdefined = {
186 [whatever] = collectpositions,
187 }
188 }
189}
190
191function drivers.converters.resyncbox(n)
192 local b = getbox(n)
193 analyze(driver,b)
194 for name, position in next, positions do
195 local xlast = { }
196 local aligned = false
197 for c=position.min,position.max do
198 local column = position[c]
199 if column then
200 local min = column.min
201 if min then
202 local max = column.max
203 local xlimit = 0
204 for r=min,max do
205 local cell = column[r]
206 if cell and cell[3].kind == "sync" then
207 local x = cell[1]
208 local l = xlast[r]
209 if l and l ~= 0 then
210 x = x + l
211 cell[1] = x
212 end
213 if x > xlimit then
214 xlimit = x
215 end
216 if not aligned then
217 aligned = cell[3].align
218 end
219 end
220 end
221 for r=min,max do
222 local cell = column[r]
223 if cell and cell[3].kind == "sync" then
224 local progress = xlimit - cell[1]
225 if aligned or progress ~= 0 then
226 local kern = new_kern(progress)
227 local current = cell[4]
228 setattributelist(kern,current)
229 insertafter(current,current,kern)
230 cell[5] = progress
231 cell[6] = kern
232 xlast[r] = (xlast[r] or 0) + progress
233 end
234 end
235 end
236 end
237 end
238 end
239
240 if aligned then
241 local min = position.min
242 local max = position.max
243 local previous = { }
244 for c=min,max do
245 local column = position[c]
246 if column then
247 local min = column.min
248 if min then
249 local max = column.max
250 for r=min,max do
251 local cell = column[r]
252 if cell then
253 local prev = previous[r]
254 if prev then
255 local align = prev[3].align
256 if align then
257 local p = prev[6]
258 local n = cell[6]
259 local d = cell[5]
260 if align == "r" or align == v_right then
261 setwidth(p,getwidth(p)+d)
262 setwidth(n,getwidth(n)-d)
263 elseif align == "c" or align == "m" or align == v_middle then
264 setwidth(p,getwidth(p)+d/2)
265 setwidth(n,getwidth(n)-d/2)
266 end
267 end
268 end
269 previous[r] = cell
270 end
271 end
272 end
273 end
274 end
275 end
276
277 end
278 return b
279end
280
281
282
283implement {
284 name = "pushlocalanchors",
285 public = true,
286 protected = true,
287 untraced = true,
288 actions = pushpositions,
289}
290
291implement {
292 name = "poplocalanchors",
293 public = true,
294 protected = true,
295 untraced = true,
296 actions = poppositions,
297}
298
299implement {
300 name = "analyzelocalanchors",
301 arguments = { "integerargument" },
302 public = true,
303 protected = true,
304 untraced = true,
305 actions = function(n)
306 analyze(driver,texgetbox(n))
307 end
308}
309
310implement {
311 name = "synchronizelocalanchors",
312 arguments = { "integerargument" },
313 public = true,
314 protected = true,
315 untraced = true,
316 actions = drivers.converters.resyncbox,
317}
318
319implement {
320 name = "setlocalsyncanchor",
321 arguments = { "argument", "integerargument", "integerargument" },
322 public = true,
323 protected = true,
324 usage = "value",
325 actions = function(name,x,y)
326
327 context(new_usernode(whatever,{ name = name, kind = "sync", x = x, y = y }))
328 end
329}
330
331implement {
332 name = "setlocalalignanchor",
333 arguments = { "argument", "integerargument", "integerargument", "argument" },
334 public = true,
335 protected = true,
336 usage = "value",
337 actions = function(name,x,y,align)
338
339 context(new_usernode(whatever,{ name = name, kind = "sync", x = x, y = y, align = align }))
340 end
341}
342
343implement {
344 name = "setlocalmarkanchor",
345 arguments = { "argument", "integerargument", "integerargument" },
346 public = true,
347 protected = true,
348 usage = "value",
349 actions = function(name,x,y)
350 context(new_usernode(whatever,{ name = name, kind = "mark", x = x, y = y }))
351 end
352}
353
354implement {
355 name = "localanchorx",
356 arguments = { "argument", "integerargument", "integerargument" },
357 public = true,
358 usage = "value",
359 actions = function(name,x,y)
360 return dimension_value, anchorx(name,x,y)
361 end
362}
363
364implement {
365 name = "localanchory",
366 arguments = { "argument", "integerargument", "integerargument" },
367 public = true,
368 usage = "value",
369 actions = function(name,x,y)
370 return dimension_value, anchory(name,x,y)
371 end
372}
373
374interfaces.implement {
375 name = "sync",
376 arguments = { "argument", "integerargument" },
377 protected = true,
378 public = true,
379 actions = function(name,x)
380 local t = positions[name].cnt
381 local y = (t[x] or 0) + 1
382 t[x] = y
383 context(new_usernode(whatever,{ name = name, kind = "sync", x = x, y = y }))
384 end,
385}
386
387interfaces.implement {
388 name = "async",
389 arguments = { "argument", "integerargument", "argument" },
390 protected = true,
391 untraced = true,
392 public = true,
393 actions = function(name,x,align)
394 local t = positions[name].cnt
395 local y = (t[x] or 0) + 1
396 t[x] = y
397 context(new_usernode(whatever,{ name = name, kind = "sync", x = x, y = y, align = align }))
398 end,
399}
400
401
402
403do
404
405 local injectors = mp.inject
406 local scanners = mp.scan
407
408 local injectnumeric = injectors.numeric
409 local injectpair = injectors.pair
410 local injectpath = injectors.path
411
412 local scaninteger = scanners.integer
413 local scanstring = scanners.string
414
415 local bpfactor = number.dimenfactors.bp
416
417 local registerscript = metapost.registerscript
418 local registerdirect = metapost.registerdirect
419
420 registerscript("anchorxy", function()
421 local x, y = anchorxy(scanstring(),scaninteger(),scaninteger())
422 return injectpair(x*bpfactor,y*bpfactor)
423 end)
424
425 registerdirect("anchorx", function() return anchorx(scanstring(),scaninteger(),scaninteger()) * bpfactor end)
426 registerdirect("anchory", function() return anchory(scanstring(),scaninteger(),scaninteger()) * bpfactor end)
427
428 registerdirect("anchorht", function() return anchorht(scanstring(),scaninteger(),scaninteger()) * bpfactor end)
429 registerdirect("anchordp", function() return anchordp(scanstring(),scaninteger(),scaninteger()) * bpfactor end)
430
431 local function corner(f)
432 local x, y = f(scanstring(),scaninteger(),scaninteger())
433 return injectpair(x*bpfactor,y*bpfactor)
434 end
435
436 registerdirect("anchorlr", function() return corner(anchorlr) end)
437 registerdirect("anchorur", function() return corner(anchorur) end)
438 registerdirect("anchorul", function() return corner(anchorul) end)
439 registerdirect("anchorll", function() return corner(anchorll) end)
440
441 registerscript("anchorbox", function()
442 local l = valid(scanstring(),scaninteger(),scaninteger())
443 local r = valid(scanstring(),scaninteger(),scaninteger())
444 local llx, lly, urx, ury, llb, urb
445 if l and r then
446 llx = l[1]
447 lly = l[2]
448 urx = r[1]
449 ury = r[2]
450 llb = l[7]
451 urb = r[7]
452 if llx > urx then
453 llx, urx = urx, llx
454 end
455 if lly > ury then
456 lly, ury = ury, lly
457 lly = lly - urb[3]
458 ury = ury + llb[2]
459 else
460 lly = lly - llb[3]
461 ury = ury + urb[2]
462 end
463 llx = llx * bpfactor
464 lly = lly * bpfactor
465 urx = urx * bpfactor
466 ury = ury * bpfactor
467 else
468 llx = 0
469 lly = 0
470 urx = 0
471 ury = 0
472 end
473 local p = {
474 cycle = true,
475 curled = true,
476 { llx, lly },
477 { urx, lly },
478 { urx, ury },
479 { llx, ury }
480 }
481 injectpath(p)
482 end)
483
484
485
486
487
488
489 local min = math.min
490 local max = math.max
491
492 registerscript("anchorspan", function()
493 local l = valid(scanstring(),scaninteger(),scaninteger())
494 local r = valid(scanstring(),scaninteger(),scaninteger())
495 local llx, lly, urx, ury, lb, ub
496 if l and r then
497 lb = l[7]
498 rb = r[7]
499 llx = min((l[1] ),(r[1] )) * bpfactor
500 lly = min((l[2] - lb[3]),(r[2] - rb[3])) * bpfactor
501 urx = max((l[1] + lb[1]),(r[1] + rb[1])) * bpfactor
502 ury = max((l[2] + lb[2]),(r[2] + rb[2])) * bpfactor
503 else
504 llx = 0
505 lly = 0
506 urx = 0
507 ury = 0
508 end
509 local p = {
510 cycle = true,
511
512 { llx, lly },
513 { urx, lly },
514 { urx, ury },
515 { llx, ury }
516 }
517 injectpath(p)
518 end)
519
520end
521 |