1if not modules then modules = { } end modules ['x-flow'] = {
2 version = 1.001,
3 comment = "companion to m-flow.mkvi",
4 author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
5 copyright = "PRAGMA ADE / ConTeXt Development Team",
6 license = "see context related readme files"
7}
8
9
10
11
12
13
14
15local type, tonumber, rawget, next = type, tonumber, rawget, next
16local gsub, find, lower = string.gsub, string.find, string.lower
17local P, S, C, Cc, lpegmatch = lpeg.P, lpeg.S, lpeg.C, lpeg.Cc, lpeg.match
18
19local context = context
20
21local ctx_startgraphic = metapost.startgraphic
22local ctx_stopgraphic = metapost.stopgraphic
23local ctx_tographic = metapost.tographic
24
25local formatters = string.formatters
26local setmetatableindex = table.setmetatableindex
27local settings_to_hash = utilities.parsers.settings_to_hash
28
29moduledata.charts = moduledata.charts or { }
30
31local report_chart = logs.reporter("chart")
32
33local variables = interfaces.variables
34local implement = interfaces.implement
35
36local v_yes = variables.yes
37local v_no = variables.no
38local v_none = variables.none
39local v_standard = variables.standard
40local v_overlay = variables.overlay
41local v_round = variables.round
42local v_test = variables.test
43
44local defaults = {
45 chart = {
46 name = "",
47 option = "",
48 backgroundcolor = "",
49 width = 100*65536,
50 height = 50*65536,
51 dx = 30*65536,
52 dy = 30*65536,
53 offset = 0,
54 bodyfont = "",
55 dot = "",
56 hcompact = variables_no,
57 vcompact = variables_no,
58 autofocus = "",
59 focus = "",
60 labeloffset = 5*65536,
61 commentoffset = 5*65536,
62 exitoffset = 0,
63
64 },
65 shape = {
66 rulethickness = 65536,
67 default = "",
68 framecolor = "darkblue",
69 backgroundcolor = "lightgray",
70 },
71 focus = {
72 rulethickness = 65536,
73 framecolor = "darkred",
74 backgroundcolor = "gray",
75 },
76 line = {
77 rulethickness = 65536,
78 radius = 10*65536,
79 color = "darkgreen",
80 corner = "",
81 dash = "",
82 arrow = "",
83 offset = "",
84 },
85 set = {
86 },
87 split = {
88 nx = 3,
89 ny = 3,
90 command = "",
91 marking = "",
92 before = "",
93 after = "",
94 }
95}
96
97local validshapes = {
98 ["node"] = { kind = "shape", number = 0 },
99 ["action"] = { kind = "shape", number = 24 },
100 ["procedure"] = { kind = "shape", number = 5 },
101 ["product"] = { kind = "shape", number = 12 },
102 ["decision"] = { kind = "shape", number = 14 },
103 ["archive"] = { kind = "shape", number = 19 },
104 ["loop"] = { kind = "shape", number = 35 },
105 ["wait"] = { kind = "shape", number = 6 },
106 ["subprocedure"] = { kind = "shape", number = 20 },
107 ["singledocument"] = { kind = "shape", number = 32 },
108 ["multidocument"] = { kind = "shape", number = 33 },
109
110 ["right"] = { kind = "line", number = 66 },
111 ["left"] = { kind = "line", number = 67 },
112 ["up"] = { kind = "line", number = 68 },
113 ["down"] = { kind = "line", number = 69 },
114}
115
116local validlabellocations = {
117 l = "l", left = "l",
118 r = "r", right = "r",
119 t = "t", top = "t",
120 b = "b", bottom = "b",
121 lt = "lt",
122 rt = "rt",
123 lb = "lb",
124 rb = "rb",
125 tl = "tl",
126 tr = "tr",
127 bl = "bl",
128 br = "br",
129}
130
131local validcommentlocations = {
132 l = "l", left = "l",
133 r = "r", right = "r",
134 t = "t", top = "t",
135 b = "b", bottom = "b",
136 lt = "lt",
137 rt = "rt",
138 lb = "lb",
139 rb = "rb",
140 tl = "tl",
141 tr = "tr",
142 bl = "bl",
143 br = "br",
144}
145
146local validtextlocations = {
147 l = "l", left = "l",
148 r = "r", right = "r",
149 t = "t", top = "t",
150 b = "b", bottom = "b",
151 c = "c", center = "c",
152 m = "c", middle = "m",
153 lt = "lt",
154 rt = "rt",
155 lb = "lb",
156 rb = "rb",
157 tl = "lt",
158 tr = "rt",
159 bl = "lb",
160 br = "rb",
161}
162
163setmetatableindex(validshapes,function(t,k)
164 local l = gsub(lower(k)," ","")
165 local v = rawget(t,l)
166 if not v then
167 local n = tonumber(k)
168 if n then
169 v = { kind = "shape", number = n }
170 else
171 v = rawget(t,"action")
172 end
173 end
174 t[k] = v
175 return v
176end)
177
178local charts = { }
179
180local data, hash, temp, last_x, last_y, name
181
182implement {
183 name = "flow_start_chart",
184 arguments = "string",
185 actions = function(chartname)
186 data = { }
187 hash = { }
188 last_x, last_y = 0, 0
189 name = chartname
190 end
191}
192
193implement {
194 name = "flow_stop_chart",
195 actions = function()
196 charts[name] = {
197 data = data,
198 hash = hash,
199 last_x = last_x,
200 last_y = last_y,
201 }
202 data, hash, temp = nil, nil, nil
203 end
204}
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232implement {
233 name = "flow_reset",
234 actions = function()
235 charts[name] = nil
236 end
237}
238
239implement {
240 name = "flow_set_current_cell",
241 arguments = "integer",
242 actions = function(n)
243 temp = data[n] or { }
244 end
245}
246
247implement {
248 name = "flow_start_cell",
249 arguments = {
250 {
251 { "shape", {
252 { "rulethickness", "dimension" },
253 { "default" },
254 { "framecolor" },
255 { "backgroundcolor" },
256 },
257 },
258 { "focus", {
259 { "rulethickness", "dimension" },
260 { "framecolor" },
261 { "backgroundcolor" },
262 },
263 },
264 { "line", {
265 { "rulethickness", "dimension" },
266 { "radius", "dimension" },
267 { "color" },
268 { "corner" },
269 { "dash" },
270 { "arrow" },
271 { "offset", "dimension" },
272 },
273 },
274 },
275 },
276 actions = function(settings)
277 temp = {
278 texts = { },
279 labels = { },
280 exits = { },
281 connections = { },
282 settings = settings,
283 x = 1,
284 y = 1,
285 realx = 1,
286 realy = 1,
287 name = "",
288 }
289 end
290}
291
292implement {
293 name = "flow_stop_cell",
294 actions = function()
295 data[#data+1] = temp
296 hash[temp.name or #data] = temp
297 end
298}
299
300implement {
301 name = "flow_set_name",
302 arguments = "string",
303 actions = function(str)
304 temp.name = str
305 end
306}
307
308implement {
309 name = "flow_set_shape",
310 arguments = "string",
311 actions = function(str)
312 temp.shape = str
313 end
314}
315
316implement {
317 name = "flow_set_destination",
318 arguments = "string",
319 actions = function(str)
320 temp.destination = str
321 end
322}
323
324implement {
325 name = "flow_set_text",
326 arguments = "2 strings",
327 actions = function(align,str)
328 temp.texts[#temp.texts+1] = {
329 align = align,
330 text = str,
331 }
332 end
333}
334
335implement {
336 name = "flow_set_overlay",
337 arguments = "string",
338 actions = function(str)
339 temp.overlay = str
340 end
341}
342
343implement {
344 name = "flow_set_focus",
345 arguments = "string",
346 actions = function(str)
347 temp.focus = str
348 end
349}
350
351implement {
352 name = "flow_set_figure",
353 arguments = "string",
354 actions = function(str)
355 temp.figure = str
356 end
357}
358
359implement {
360 name = "flow_set_label",
361 arguments = "2 strings",
362 actions = function(location,text)
363 temp.labels[#temp.labels+1] = {
364 location = location,
365 text = text,
366 }
367 end
368}
369
370implement {
371 name = "flow_set_comment",
372 arguments = "2 strings",
373 actions = function(location,text)
374 local connections = temp.connections
375 if connections then
376 local connection = connections[#connections]
377 if connection then
378 local comments = connection.comments
379 if comments then
380 comments[#comments+1] = {
381 location = location,
382 text = text,
383 }
384 end
385 end
386 end
387 end
388}
389
390implement {
391 name = "flow_set_exit",
392 arguments = "2 strings",
393 actions = function(location,text)
394 temp.exits[#temp.exits+1] = {
395 location = location,
396 text = text,
397 }
398 end
399}
400
401implement {
402 name = "flow_set_include",
403 arguments = { "string", "integer", "integer", "string" },
404 actions = function(name,x,y,settings)
405 data[#data+1] = {
406 include = name,
407 x = x,
408 y = y,
409
410 }
411 end
412}
413
414local function inject(includedata,data,hash)
415 local subchart = charts[includedata.include]
416 if not subchart then
417 return
418 end
419 local subdata = subchart.data
420 if not subdata then
421 return
422 end
423 local xoffset = (includedata.x or 1) - 1
424 local yoffset = (includedata.y or 1) - 1
425 local settings = includedata.settings
426 for i=1,#subdata do
427 local si = subdata[i]
428 if si.include then
429 inject(si,data,hash)
430 else
431 local x = si.x + xoffset
432 local y = si.y + yoffset
433 local t = {
434 x = x,
435 y = y,
436 realx = x,
437 realy = y,
438 settings = settings,
439 }
440 setmetatableindex(t,si)
441 data[#data+1] = t
442 hash[si.name or #data] = t
443 end
444 end
445end
446
447local function pack(data,field)
448 local list, max = { }, 0
449 for e=1,#data do
450 local d = data[e]
451 local f = d[field]
452 list[f] = true
453 if f > max then
454 max = f
455 end
456 end
457 for i=1,max do
458 if not list[i] then
459 for e=1,#data do
460 local d = data[e]
461 local f = d[field]
462 if f > i then
463 d[field] = f - 1
464 end
465 end
466 end
467 end
468end
469
470local function expanded(chart,chartsettings)
471 local expandeddata = { }
472 local expandedhash = { }
473 local expandedchart = {
474 data = expandeddata,
475 hash = expandedhash,
476 }
477 setmetatableindex(expandedchart,chart)
478 local data = chart.data
479 local hash = chart.hash
480 for i=1,#data do
481 local di = data[i]
482 if di.include then
483 inject(di,expandeddata,expandedhash)
484 else
485 expandeddata[#expandeddata+1] = di
486 expandedhash[di.name or #expandeddata] = di
487 end
488 end
489
490 expandedchart.settings = chartsettings or { }
491
492 chartsettings.shape = chartsettings.shape or { }
493 chartsettings.focus = chartsettings.focus or { }
494 chartsettings.line = chartsettings.line or { }
495 chartsettings.set = chartsettings.set or { }
496 chartsettings.split = chartsettings.split or { }
497 chartsettings.chart = chartsettings.chart or { }
498 setmetatableindex(chartsettings.shape,defaults.shape)
499 setmetatableindex(chartsettings.focus,defaults.focus)
500 setmetatableindex(chartsettings.line ,defaults.line )
501 setmetatableindex(chartsettings.set ,defaults.set )
502 setmetatableindex(chartsettings.split,defaults.split)
503 setmetatableindex(chartsettings.chart,defaults.chart)
504
505 if chartsettings.chart.vcompact == v_yes then
506 pack(expandeddata,"y")
507 end
508 if chartsettings.chart.hcompact == v_yes then
509 pack(expandeddata,"x")
510 end
511
512 for i=1,#expandeddata do
513 local cell = expandeddata[i]
514 local settings = cell.settings
515 if not settings then
516 cell.settings = chartsettings
517 else
518 settings.shape = settings.shape or { }
519 settings.focus = settings.focus or { }
520 settings.line = settings.line or { }
521 setmetatableindex(settings.shape,chartsettings.shape)
522 setmetatableindex(settings.focus,chartsettings.focus)
523 setmetatableindex(settings.line ,chartsettings.line)
524 end
525 end
526 return expandedchart
527end
528
529local splitter = lpeg.splitat(",")
530
531implement {
532 name = "flow_set_location",
533 arguments = "string",
534 actions = function(x,y)
535 if type(x) == "string" and not y then
536 x, y = lpegmatch(splitter,x)
537 end
538 local oldx, oldy = x, y
539 if not x or x == "" then
540 x = last_x
541 elseif type(x) == "number" then
542
543 elseif x == "+" then
544 x = last_x + 1
545 elseif x == "-" then
546 x = last_x - 1
547 elseif find(x,"^[%+%-]") then
548 x = last_x + (tonumber(x) or 0)
549 else
550 x = tonumber(x)
551 end
552 if not y or y == "" then
553 y = last_y
554 elseif type(y) == "number" then
555
556 elseif y == "+" then
557 y = last_y + 1
558 elseif x == "-" then
559 y = last_y - 1
560 elseif find(y,"^[%+%-]") then
561 y = last_y + (tonumber(y) or 0)
562 else
563 y = tonumber(y)
564 end
565 if x < 1 or y < 1 then
566 report_chart("the cell (%s,%s) ends up at (%s,%s) and gets relocated to (1,1)",oldx or"?", oldy or "?", x,y)
567 if x < 1 then
568 x = 1
569 end
570 if y < 1 then
571 y = 1
572 end
573 end
574 temp.x = x or 1
575 temp.y = y or 1
576 temp.realx = x or 1
577 temp.realy = y or 1
578 last_x = x or last_x
579 last_y = y or last_y
580 end
581}
582
583implement {
584 name = "flow_set_connection",
585 arguments = "3 strings",
586 actions = function(location,displacement,name)
587 local dx, dy = lpegmatch(splitter,displacement)
588 dx = tonumber(dx)
589 dy = tonumber(dy)
590 temp.connections[#temp.connections+1] = {
591 location = location,
592 dx = dx or 0,
593 dy = dy or 0,
594 name = name,
595 comments = { },
596 }
597 end
598}
599
600local function visible(chart,cell)
601 local x, y = cell.x, cell.y
602 return
603 x >= chart.from_x and x <= chart.to_x and
604 y >= chart.from_y and y <= chart.to_y and cell
605end
606
607local function process_cells(g,chart,xoffset,yoffset)
608 local data = chart.data
609 if not data then
610 return
611 end
612 local focus = settings_to_hash(chart.settings.chart.focus or "")
613 for i=1,#data do
614 local cell = visible(chart,data[i])
615 if cell then
616 local settings = cell.settings
617 local shapesettings = settings.shape
618 local shape = cell.shape
619 if not shape or shape == "" then
620 shape = shapesettings.default or "none"
621 end
622 if shape ~= v_none then
623 local shapedata = validshapes[shape]
624 ctx_tographic(g,"flow_begin_sub_chart ;")
625 if shapedata.kind == "line" then
626 local linesettings = settings.line
627 ctx_tographic(g,"flow_shape_line_color := %q ;", linesettings.color)
628 ctx_tographic(g,"flow_shape_fill_color := %q ;","black")
629 ctx_tographic(g,"flow_shape_line_width := %p ; ",linesettings.rulethickness)
630 elseif focus[cell.focus] or focus[cell.name] then
631 local focussettings = settings.focus
632 ctx_tographic(g,"flow_shape_line_color := %q ;", focussettings.framecolor)
633 ctx_tographic(g,"flow_shape_fill_color := %q ;", focussettings.backgroundcolor)
634 ctx_tographic(g,"flow_shape_line_width := %p ; ",focussettings.rulethickness)
635 else
636 local shapesettings = settings.shape
637 ctx_tographic(g,"flow_shape_line_color := %q ;", shapesettings.framecolor)
638 ctx_tographic(g,"flow_shape_fill_color := %q ;", shapesettings.backgroundcolor)
639 ctx_tographic(g,"flow_shape_line_width := %p ; ",shapesettings.rulethickness)
640 end
641 ctx_tographic(g,"flow_peepshape := false ;")
642 ctx_tographic(g,"flow_new_shape(%s,%s,%s) ;",cell.x+xoffset,cell.y+yoffset,shapedata.number)
643 ctx_tographic(g,"flow_end_sub_chart ;")
644 end
645 end
646 end
647end
648
649
650
651local sign = S("+p") / "1"
652 + S("-mn") / "-1"
653
654local full = C(P("left"))
655 + C(P("right"))
656 + C(P("top"))
657 + C(P("bottom"))
658
659local char = P("l") / "left"
660 + P("r") / "right"
661 + P("t") / "top"
662 + P("b") / "bottom"
663
664local space = P(" ")^0
665
666local what = space
667 * (sign + Cc("0"))
668 * space
669 * (full + char)
670 * space
671 * (sign + Cc("0"))
672 * space
673 * (full + char)
674 * space
675 * P(-1)
676
677
678
679
680
681
682local function process_connections(g,chart,xoffset,yoffset)
683 local data = chart.data
684 local hash = chart.hash
685 if not data then
686 return
687 end
688 local settings = chart.settings
689 for i=1,#data do
690 local cell = visible(chart,data[i])
691 if cell then
692 local connections = cell.connections
693 for j=1,#connections do
694 local connection = connections[j]
695 local othername = connection.name
696 local othercell = hash[othername]
697 if othercell then
698 local cellx, celly = cell.x, cell.y
699 local otherx, othery, location = othercell.x, othercell.y, connection.location
700 if otherx > 0 and othery > 0 and cellx > 0 and celly > 0 and location then
701 local what_cell, where_cell, what_other, where_other = lpegmatch(what,location)
702 if what_cell and where_cell and what_other and where_other then
703 local linesettings = settings.line
704 ctx_tographic(g,"flow_smooth := %s ;", linesettings.corner == v_round and "true" or "false")
705 ctx_tographic(g,"flow_dashline := %s ;", linesettings.dash == v_yes and "true" or "false")
706 ctx_tographic(g,"flow_arrowtip := %s ;", linesettings.arrow == v_yes and "true" or "false")
707 ctx_tographic(g,"flow_touchshape := %s ;", linesettings.offset == v_none and "true" or "false")
708 ctx_tographic(g,"flow_dsp_x := %s ; flow_dsp_y := %s ;",connection.dx or 0, connection.dy or 0)
709 ctx_tographic(g,"flow_connection_line_color := %q ;",linesettings.color)
710 ctx_tographic(g,"flow_connection_line_width := %p ;",linesettings.rulethickness)
711 ctx_tographic(g,"flow_connect_%s_%s (%s) (%s,%s,%s) (%s,%s,%s) ;",where_cell,where_other,j,cellx,celly,what_cell,otherx,othery,what_other)
712 ctx_tographic(g,"flow_dsp_x := 0 ; flow_dsp_y := 0 ;")
713 end
714 end
715 end
716 end
717 end
718 end
719end
720
721local f_texttemplate_t = formatters["\\setvariables[flowcell:text][x=%s,y=%s,n=%i,align={%s},figure={%s},overlay={%s},destination={%s},realx=%s,realy=%s]"]
722local f_texttemplate_l = formatters["\\doFLOWlabel{%i}{%i}{%i}{%i}{%i}"]
723
724local splitter = lpeg.splitat(":")
725local charttexts = { }
726
727implement {
728 name = "flow_get_text",
729 arguments = "integer",
730 actions = function(n)
731 if n > 0 then
732 context(charttexts[n])
733 end
734 end
735}
736
737local function process_texts(g,chart,xoffset,yoffset)
738 local data = chart.data
739 local hash = chart.hash
740 if not data then
741 return
742 end
743 charttexts = { }
744 for i=1,#data do
745 local cell = visible(chart,data[i])
746 if cell then
747 local x = cell.x or 1
748 local y = cell.y or 1
749 local figure = cell.figure or ""
750 local overlay = cell.overlay or ""
751 local destination = cell.destination or ""
752 local texts = cell.texts
753 local noftexts = #texts
754 local realx = cell.realx or x
755 local realy = cell.realy or y
756 if noftexts > 0 then
757 for i=1,noftexts do
758 local text = texts[i]
759 local data = text.text
760 local align = text.align or ""
761 local align = validlabellocations[align] or align
762 charttexts[#charttexts+1] = data
763 ctx_tographic(g,'flow_chart_draw_text(%s,%s,textext("%s")) ;',x,y,f_texttemplate_t(x,y,#charttexts,align,figure,overlay,destination,realx,realy))
764 if i == 1 then
765 figure = ""
766 overlay = ""
767 destination = ""
768 end
769 end
770 elseif figure ~= "" or overlay ~= "" or destination ~= "" then
771 ctx_tographic(g,'flow_chart_draw_text(%s,%s,textext("%s")) ;',x,y,f_texttemplate_t(x,y,0,"",figure,overlay,destination,realx,realy))
772 end
773 local labels = cell.labels
774 for i=1,#labels do
775 local label = labels[i]
776 local text = label.text
777 local location = label.location or ""
778 local location = validlabellocations[location] or location
779 if text and text ~= "" then
780 charttexts[#charttexts+1] = text
781 ctx_tographic(g,'flow_chart_draw_label(%s,%s,"%s",textext("%s")) ;',x,y,location,f_texttemplate_l(x,y,#charttexts,realx,realy))
782 end
783 end
784 local exits = cell.exits
785 for i=1,#exits do
786 local exit = exits[i]
787 local text = exit.text
788 local location = exit.location or ""
789 local location = validlabellocations[location] or location
790 if text ~= "" then
791
792 if location == "l" and x == chart.from_x + 1 or
793 location == "r" and x == chart.to_x - 1 or
794 location == "t" and y == chart.to_y - 1 or
795 location == "b" and y == chart.from_y + 1 then
796 charttexts[#charttexts+1] = text
797 ctx_tographic(g,'flow_chart_draw_exit(%s,%s,"%s",textext("%s")) ;',x,y,location,f_texttemplate_l(x,y,#charttexts,realx,realy))
798 end
799 end
800 end
801 local connections = cell.connections
802 for i=1,#connections do
803 local comments = connections[i].comments
804 for j=1,#comments do
805 local comment = comments[j]
806 local text = comment.text
807 local location = comment.location or ""
808 local length = 0
809
810 local loc, len = lpegmatch(splitter,location)
811 if len == "*" then
812 location = validcommentlocations[loc] or ""
813 if location == "" then
814 location = "*"
815 else
816 location = location .. ":*"
817 end
818 elseif loc then
819 location = validcommentlocations[loc] or "*"
820 length = tonumber(len) or 0
821 else
822 location = validcommentlocations[location] or ""
823 end
824 if text and text ~= "" then
825 charttexts[#charttexts+1] = text
826 ctx_tographic(g,'flow_chart_draw_comment(%s,%s,%s,"%s",%s,textext("%s")) ;',x,y,i,location,length,f_texttemplate_l(x,y,#charttexts,realx,realy))
827 end
828 end
829 end
830 end
831 end
832end
833
834local function getchart(settings,forced_x,forced_y,forced_nx,forced_ny)
835 if not settings then
836 print("no settings given")
837 return
838 end
839 local chartname = settings.chart.name
840 if not chartname then
841 print("no name given")
842 return
843 end
844 local chart = charts[chartname]
845 if not chart then
846 print("no such chart",chartname)
847 return
848 end
849 chart = expanded(chart,settings)
850 local chartsettings = chart.settings.chart
851 local autofocus = chart.settings.chart.autofocus
852 if autofocus then
853 autofocus = settings_to_hash(autofocus)
854 if not next(autofocus) then
855 autofocus = false
856 end
857 end
858
859 local x = forced_x or tonumber(chartsettings.x)
860 local y = forced_y or tonumber(chartsettings.y)
861 local nx = forced_nx or tonumber(chartsettings.nx)
862 local ny = forced_ny or tonumber(chartsettings.ny)
863
864 local minx, miny, maxx, maxy = 0, 0, 0, 0
865 local data = chart.data
866 for i=1,#data do
867 local cell = data[i]
868 if not autofocus or autofocus[cell.name] then
869 local x = cell.realx
870 local y = cell.realy
871 if minx == 0 or x < minx then minx = x end
872 if miny == 0 or y < miny then miny = y end
873 if minx == 0 or x > maxx then maxx = x end
874 if miny == 0 or y > maxy then maxy = y end
875 end
876 end
877
878 if x + nx > maxx then
879 nx = maxx - x + 1
880 end
881 if y + ny > maxy then
882 ny = maxy - y + 1
883 end
884
885
886 if autofocus then
887
888 if nx and nx > 0 then
889 maxx = minx + nx - 1
890 end
891 if ny and ny > 0 then
892 maxy = miny + ny - 1
893 end
894 else
895 if x and x > 0 then
896 minx = x
897 end
898 if y and y > 0 then
899 miny = y
900 end
901 if nx and nx > 0 then
902 maxx = minx + nx - 1
903 end
904 if ny and ny > 0 then
905 maxy = miny + ny - 1
906 end
907 end
908
909
910 local nx = maxx - minx + 1
911 local ny = maxy - miny + 1
912
913 for i=1,#data do
914 local cell = data[i]
915 cell.x = cell.realx - minx + 1
916 cell.y = cell.realy - miny + 1
917 end
918 chart.from_x = 1
919 chart.from_y = 1
920 chart.to_x = nx
921 chart.to_y = ny
922 chart.nx = nx
923 chart.ny = ny
924
925 chart.shift_x = minx + 1
926 chart.shift_y = miny + 1
927
928 return chart
929end
930
931local function makechart_indeed(chart)
932 local settings = chart.settings
933 local chartsettings = settings.chart
934
935 local g = ctx_startgraphic {
936 instance = "metafun",
937 format = "metafun",
938 method = "scaled",
939 definitions = "",
940 wrapped = true,
941 }
942
943 ctx_tographic(g,"if unknown context_flow : input mp-char.mpiv ; fi ;")
944 ctx_tographic(g,"flow_begin_chart(0,%s,%s);",chart.nx,chart.ny)
945
946 if chartsettings.option == v_test or chartsettings.dot == v_yes then
947 ctx_tographic(g,"flow_show_con_points := true ;")
948 ctx_tographic(g,"flow_show_mid_points := true ;")
949 ctx_tographic(g,"flow_show_all_points := true ;")
950 elseif chartsettings.dot ~= "" then
951 ctx_tographic(g,"flow_show_%s_points := true ;",chartsettings.dot)
952 end
953
954 local backgroundcolor = chartsettings.backgroundcolor
955 if backgroundcolor and backgroundcolor ~= "" then
956 ctx_tographic(g,"flow_chart_background_color := %q ;",backgroundcolor)
957 end
958
959 local shapewidth = chartsettings.width
960 local gridwidth = shapewidth + 2*chartsettings.dx
961 local shapeheight = chartsettings.height
962 local gridheight = shapeheight + 2*chartsettings.dy
963 local chartoffset = chartsettings.offset
964 local labeloffset = chartsettings.labeloffset
965 local exitoffset = chartsettings.exitoffset
966 local commentoffset = chartsettings.commentoffset
967 local clipoffset = chartsettings.clipoffset
968 ctx_tographic(g,"flow_grid_width := %p ;", gridwidth)
969 ctx_tographic(g,"flow_grid_height := %p ;", gridheight)
970 ctx_tographic(g,"flow_shape_width := %p ;", shapewidth)
971 ctx_tographic(g,"flow_shape_height := %p ;", shapeheight)
972 ctx_tographic(g,"flow_chart_offset := %p ;", chartoffset)
973 ctx_tographic(g,"flow_label_offset := %p ;", labeloffset)
974 ctx_tographic(g,"flow_exit_offset := %p ;", exitoffset)
975 ctx_tographic(g,"flow_comment_offset := %p ;", commentoffset)
976
977 local radius = settings.line.radius
978 local rulethickness = settings.line.rulethickness
979 local dx = chartsettings.dx
980 local dy = chartsettings.dy
981 if radius < rulethickness then
982 radius = 2.5*rulethickness
983 if radius > dx then
984 radius = dx
985 end
986 if radius > dy then
987 radius = dy
988 end
989 end
990 ctx_tographic(g,"flow_connection_line_width := %p ;", rulethickness)
991 ctx_tographic(g,"flow_connection_smooth_size := %p ;", radius)
992 ctx_tographic(g,"flow_connection_arrow_size := %p ;", radius)
993 ctx_tographic(g,"flow_connection_dash_size := %p ;", radius)
994
995 local offset = chartsettings.offset
996 if offset == v_none or offset == v_overlay or offset == "" then
997 offset = -2.5 * radius
998 elseif offset == v_standard then
999 offset = radius
1000 end
1001 ctx_tographic(g,"flow_chart_offset := %p ;",offset)
1002 ctx_tographic(g,"flow_chart_clip_offset := %p ;",clipoffset)
1003
1004 ctx_tographic(g,"flow_reverse_y := true ;")
1005 if chartsettings.option == v_test then
1006 ctx_tographic(g,"flow_draw_test_shapes ;")
1007 end
1008
1009 process_cells(g,chart,0,0)
1010 process_connections(g,chart,0,0)
1011 process_texts(g,chart,0,0)
1012
1013
1014 ctx_tographic(g,"flow_end_chart ;")
1015 ctx_stopgraphic(g)
1016
1017end
1018
1019
1020
1021local function makechart(chart)
1022 context.hbox()
1023 context.bgroup()
1024 context.forgetall()
1025 context(function() makechart_indeed(chart) end)
1026 context.egroup()
1027end
1028
1029local function splitchart(chart)
1030 local settings = chart.settings
1031 local splitsettings = settings.split
1032 local chartsettings = settings.chart
1033
1034 local name = chartsettings.name
1035
1036 local from_x = chart.from_x
1037 local from_y = chart.from_y
1038 local to_x = chart.to_x
1039 local to_y = chart.to_y
1040
1041 local step_x = splitsettings.nx or to_x
1042 local step_y = splitsettings.ny or to_y
1043 local delta_x = splitsettings.dx or 0
1044 local delta_y = splitsettings.dy or 0
1045
1046 report_chart("spliting %a from (%s,%s) upto (%s,%s) with steps (%s,%s) and overlap (%s,%s)",
1047 name,from_x,from_y,to_x,to_y,step_x,step_y,delta_x,delta_y)
1048
1049 local part_x = 0
1050 local first_x = from_x
1051 while true do
1052 part_x = part_x + 1
1053 local last_x = first_x + step_x - 1
1054 local done = last_x >= to_x
1055 if done then
1056 last_x = to_x
1057 end
1058
1059
1060
1061 local part_y = 0
1062 local first_y = from_y
1063 while true do
1064 part_y = part_y + 1
1065 local last_y = first_y + step_y - 1
1066 local done = last_y >= to_y
1067 if done then
1068 last_y = to_y
1069 end
1070
1071
1072
1073
1074 local data = chart.data
1075 for i=1,#data do
1076 local cell = data[i]
1077
1078 local cx, cy = cell.x, cell.y
1079 if cx >= first_x and cx <= last_x then
1080 if cy >= first_y and cy <= last_y then
1081 report_chart("part (%s,%s) of %a is split from (%s,%s) -> (%s,%s)",part_x,part_y,name,first_x,first_y,last_x,last_y)
1082 local x = first_x
1083 local y = first_y
1084 local nx = last_x - first_x + 1
1085 local ny = last_y - first_y + 1
1086 context.beforeFLOWsplit()
1087 context.handleFLOWsplit(function()
1088 makechart(getchart(settings,x,y,nx,ny))
1089 end)
1090 context.afterFLOWsplit()
1091 break
1092 end
1093 end
1094 end
1095
1096 if done then
1097 break
1098 else
1099 first_y = last_y + 1 - delta_y
1100 end
1101 end
1102 if done then
1103 break
1104 else
1105 first_x = last_x + 1 - delta_x
1106 end
1107 end
1108end
1109
1110implement {
1111 name = "flow_make_chart",
1112 arguments = {
1113 {
1114 { "chart", {
1115 { "name" },
1116 { "option" },
1117 { "backgroundcolor" },
1118 { "width", "dimension" },
1119 { "height", "dimension" },
1120 { "dx", "dimension" },
1121 { "dy", "dimension" },
1122 { "offset", "dimension" },
1123
1124 { "dot" },
1125 { "hcompact" },
1126 { "vcompact" },
1127 { "focus" },
1128 { "autofocus" },
1129 { "nx", "integer" },
1130 { "ny", "integer" },
1131 { "x", "integer" },
1132 { "y", "integer" },
1133 { "clipoffset", "dimension" },
1134 { "labeloffset", "dimension" },
1135 { "commentoffset", "dimension" },
1136 { "exitoffset", "dimension" },
1137 { "split" },
1138 },
1139 },
1140 { "shape", {
1141 { "rulethickness", "dimension" },
1142 { "default" },
1143 { "framecolor" },
1144 { "backgroundcolor" },
1145 },
1146 },
1147 { "focus", {
1148 { "rulethickness", "dimension" },
1149 { "framecolor" },
1150 { "backgroundcolor" },
1151 },
1152 },
1153 { "line", {
1154 { "rulethickness", "dimension" },
1155 { "radius", "dimension" },
1156 { "color" },
1157 { "corner" },
1158 { "dash" },
1159 { "arrow" },
1160 { "offset" },
1161 },
1162 },
1163 { "split", {
1164 { "nx", "integer" },
1165 { "ny", "integer" },
1166 { "dx", "integer" },
1167 { "dy", "integer" },
1168 { "command" },
1169 { "marking" },
1170 { "before" },
1171 { "after" },
1172 },
1173 },
1174
1175 }
1176 },
1177 actions = function(settings)
1178 local chart = getchart(settings)
1179 if chart then
1180 local settings = chart.settings
1181 if settings then
1182 local chartsettings = settings.chart
1183 if chartsettings and chartsettings.split == v_yes then
1184 splitchart(chart)
1185 else
1186 makechart(chart)
1187 end
1188 else
1189 makechart(chart)
1190 end
1191 end
1192 end
1193}
1194 |