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