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
936
937
938
939
940
941
942 local g = ctx_startgraphic {
943 instance = "chart",
944 format = "metafun",
945
946
947 method = "double",
948
949 definitions = "",
950 wrapped = true,
951 }
952
953 ctx_tographic(g,"if unknown context_flow : input mp-char.mpiv ; fi ;")
954 ctx_tographic(g,"flow_begin_chart(0,%s,%s);",chart.nx,chart.ny)
955
956 if chartsettings.option == v_test or chartsettings.dot == v_yes then
957 ctx_tographic(g,"flow_show_con_points := true ;")
958 ctx_tographic(g,"flow_show_mid_points := true ;")
959 ctx_tographic(g,"flow_show_all_points := true ;")
960 elseif chartsettings.dot ~= "" then
961 ctx_tographic(g,"flow_show_%s_points := true ;",chartsettings.dot)
962 end
963
964 local backgroundcolor = chartsettings.backgroundcolor
965 if backgroundcolor and backgroundcolor ~= "" then
966 ctx_tographic(g,"flow_chart_background_color := %q ;",backgroundcolor)
967 end
968
969 local shapewidth = chartsettings.width
970 local gridwidth = shapewidth + 2*chartsettings.dx
971 local shapeheight = chartsettings.height
972 local gridheight = shapeheight + 2*chartsettings.dy
973 local chartoffset = chartsettings.offset
974 local labeloffset = chartsettings.labeloffset
975 local exitoffset = chartsettings.exitoffset
976 local commentoffset = chartsettings.commentoffset
977 local clipoffset = chartsettings.clipoffset
978 ctx_tographic(g,"flow_grid_width := %p ;", gridwidth)
979 ctx_tographic(g,"flow_grid_height := %p ;", gridheight)
980 ctx_tographic(g,"flow_shape_width := %p ;", shapewidth)
981 ctx_tographic(g,"flow_shape_height := %p ;", shapeheight)
982 ctx_tographic(g,"flow_chart_offset := %p ;", chartoffset)
983 ctx_tographic(g,"flow_label_offset := %p ;", labeloffset)
984 ctx_tographic(g,"flow_exit_offset := %p ;", exitoffset)
985 ctx_tographic(g,"flow_comment_offset := %p ;", commentoffset)
986
987 local radius = settings.line.radius
988 local rulethickness = settings.line.rulethickness
989 local dx = chartsettings.dx
990 local dy = chartsettings.dy
991 if radius < rulethickness then
992 radius = 2.5*rulethickness
993 if radius > dx then
994 radius = dx
995 end
996 if radius > dy then
997 radius = dy
998 end
999 end
1000 ctx_tographic(g,"flow_connection_line_width := %p ;", rulethickness)
1001 ctx_tographic(g,"flow_connection_smooth_size := %p ;", radius)
1002 ctx_tographic(g,"flow_connection_arrow_size := %p ;", radius)
1003 ctx_tographic(g,"flow_connection_dash_size := %p ;", radius)
1004
1005 local offset = chartsettings.offset
1006 if offset == v_none or offset == v_overlay or offset == "" then
1007 offset = -2.5 * radius
1008 elseif offset == v_standard then
1009 offset = radius
1010 end
1011 ctx_tographic(g,"flow_chart_offset := %p ;",offset)
1012 ctx_tographic(g,"flow_chart_clip_offset := %p ;",clipoffset)
1013
1014 ctx_tographic(g,"flow_reverse_y := true ;")
1015 if chartsettings.option == v_test then
1016 ctx_tographic(g,"flow_draw_test_shapes ;")
1017 end
1018
1019 process_cells(g,chart,0,0)
1020 process_connections(g,chart,0,0)
1021 process_texts(g,chart,0,0)
1022
1023
1024 ctx_tographic(g,"flow_end_chart ;")
1025 ctx_stopgraphic(g)
1026
1027end
1028
1029
1030
1031local function makechart(chart)
1032 context.hbox()
1033 context.bgroup()
1034 context.forgetall()
1035 context(function() makechart_indeed(chart) end)
1036 context.egroup()
1037end
1038
1039local function splitchart(chart)
1040 local settings = chart.settings
1041 local splitsettings = settings.split
1042 local chartsettings = settings.chart
1043
1044 local name = chartsettings.name
1045
1046 local from_x = chart.from_x
1047 local from_y = chart.from_y
1048 local to_x = chart.to_x
1049 local to_y = chart.to_y
1050
1051 local step_x = splitsettings.nx or to_x
1052 local step_y = splitsettings.ny or to_y
1053 local delta_x = splitsettings.dx or 0
1054 local delta_y = splitsettings.dy or 0
1055
1056 report_chart("spliting %a from (%s,%s) upto (%s,%s) with steps (%s,%s) and overlap (%s,%s)",
1057 name,from_x,from_y,to_x,to_y,step_x,step_y,delta_x,delta_y)
1058
1059 local part_x = 0
1060 local first_x = from_x
1061 while true do
1062 part_x = part_x + 1
1063 local last_x = first_x + step_x - 1
1064 local done = last_x >= to_x
1065 if done then
1066 last_x = to_x
1067 end
1068
1069
1070
1071 local part_y = 0
1072 local first_y = from_y
1073 while true do
1074 part_y = part_y + 1
1075 local last_y = first_y + step_y - 1
1076 local done = last_y >= to_y
1077 if done then
1078 last_y = to_y
1079 end
1080
1081
1082
1083
1084 local data = chart.data
1085 for i=1,#data do
1086 local cell = data[i]
1087
1088 local cx, cy = cell.x, cell.y
1089 if cx >= first_x and cx <= last_x then
1090 if cy >= first_y and cy <= last_y then
1091 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)
1092 local x = first_x
1093 local y = first_y
1094 local nx = last_x - first_x + 1
1095 local ny = last_y - first_y + 1
1096 context.beforeFLOWsplit()
1097 context.handleFLOWsplit(function()
1098 makechart(getchart(settings,x,y,nx,ny))
1099 end)
1100 context.afterFLOWsplit()
1101 break
1102 end
1103 end
1104 end
1105
1106 if done then
1107 break
1108 else
1109 first_y = last_y + 1 - delta_y
1110 end
1111 end
1112 if done then
1113 break
1114 else
1115 first_x = last_x + 1 - delta_x
1116 end
1117 end
1118end
1119
1120implement {
1121 name = "flow_make_chart",
1122 arguments = {
1123 {
1124 { "chart", {
1125 { "name" },
1126 { "option" },
1127 { "backgroundcolor" },
1128 { "width", "dimension" },
1129 { "height", "dimension" },
1130 { "dx", "dimension" },
1131 { "dy", "dimension" },
1132 { "offset", "dimension" },
1133
1134 { "dot" },
1135 { "hcompact" },
1136 { "vcompact" },
1137 { "focus" },
1138 { "autofocus" },
1139 { "nx", "integer" },
1140 { "ny", "integer" },
1141 { "x", "integer" },
1142 { "y", "integer" },
1143 { "clipoffset", "dimension" },
1144 { "labeloffset", "dimension" },
1145 { "commentoffset", "dimension" },
1146 { "exitoffset", "dimension" },
1147 { "split" },
1148 },
1149 },
1150 { "shape", {
1151 { "rulethickness", "dimension" },
1152 { "default" },
1153 { "framecolor" },
1154 { "backgroundcolor" },
1155 },
1156 },
1157 { "focus", {
1158 { "rulethickness", "dimension" },
1159 { "framecolor" },
1160 { "backgroundcolor" },
1161 },
1162 },
1163 { "line", {
1164 { "rulethickness", "dimension" },
1165 { "radius", "dimension" },
1166 { "color" },
1167 { "corner" },
1168 { "dash" },
1169 { "arrow" },
1170 { "offset" },
1171 },
1172 },
1173 { "split", {
1174 { "nx", "integer" },
1175 { "ny", "integer" },
1176 { "dx", "integer" },
1177 { "dy", "integer" },
1178 { "command" },
1179 { "marking" },
1180 { "before" },
1181 { "after" },
1182 },
1183 },
1184
1185 }
1186 },
1187 actions = function(settings)
1188 local chart = getchart(settings)
1189 if chart then
1190 local settings = chart.settings
1191 if settings then
1192 local chartsettings = settings.chart
1193 if chartsettings and chartsettings.split == v_yes then
1194 splitchart(chart)
1195 else
1196 makechart(chart)
1197 end
1198 else
1199 makechart(chart)
1200 end
1201 end
1202 end
1203}
1204 |