1 % elements.mp -- MetaPost macros for drawing Click configuration graphs
4 % Copyright (c) 1999-2001 Massachusetts Institute of Technology
5 % Copyright (c) 2001-2003 International Computer Science Institute
6 % Copyright (c) 2006 Regents of the University of California
8 % Permission is hereby granted, free of charge, to any person obtaining a
9 % copy of this software and associated documentation files (the "Software"),
10 % to deal in the Software without restriction, subject to the conditions
11 % listed in the Click LICENSE file. These conditions include: you must
12 % preserve this copyright notice, and you cannot mention the copyright
13 % holders in advertising related to the Software without their permission.
14 % The Software is provided WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED. This
15 % notice is a summary of the Click LICENSE file; the license in that file is
20 string defaultelementfont;
25 element_offset = (7.5, 4.5);
26 min_element_height = 19;
27 element_height_increment = 4;
29 defaultelementborderscale = 1;
30 defaultelementportscale = 1;
49 pen elementpen.border, elementpen.port, connectionpen;
50 elementpen.border = pencircle scaled 0.9;
51 elementpen.port = pencircle scaled 0.35;
52 connectionpen = pencircle scaled 0.45;
54 color personalitycolor[], agnosticcolor[];
55 personalitycolor[push] = black;
56 personalitycolor[agnostic_push] = personalitycolor[agnostic] = white;
57 personalitycolor[pull] = personalitycolor[agnostic_pull] = white;
58 agnosticcolor[agnostic_push] = black;
59 agnosticcolor[agnostic_pull] = white;
60 agnosticcolor[agnostic] = 0.6white;
62 path _agnostic_output, _agnostic_input, _normal_output, _normal_input;
63 _agnostic_output := ((-.5,0.5output_length-agnostic_sep)
64 -- (-output_width+agnostic_sep,0.5output_length-agnostic_sep)
65 -- (-output_width+agnostic_sep,-0.5output_length+agnostic_sep)
66 -- (-.5,-0.5output_length+agnostic_sep) -- cycle);
67 _agnostic_input := ((.5,0.5input_length-1.414agnostic_sep)
68 -- (input_width-1.414agnostic_sep,0)
69 -- (.5,-0.5input_length+1.414agnostic_sep) -- cycle);
70 _normal_input := ((.5,0.5input_length) -- (input_width,0)
71 -- (.5,-0.5input_length) -- cycle);
72 _normal_output := ((-.5,0.5output_length) -- (-output_width,0.5output_length)
73 -- (-output_width,-0.5output_length) -- (-.5,-0.5output_length) -- cycle);
76 %% redefine 'drawboxes' to allow extra text
78 vardef drawboxes(text t) text rest = % Draw boundary path for each box
79 forsuffixes s=t: draw bpath.s rest; endfor
85 vardef _make_element_ports(suffix $, port, side)(expr n, length, isout) =
86 save _i_, _sc; pair _sc.adj;
87 _sc.sep = (length - 2*port_offset - n*port_length + port_sep) / n;
88 _sc.delta = port_length + _sc.sep;
89 _sc = length/2 - port_offset - (_sc.sep - port_sep)/2 - 0.5port_length;
90 _sc.adj = if isout: 1/2$.flowvector else: -1/2$.flowvector fi;
92 $.port[_i_] = $.side + $.sidevector * (_sc - _sc.delta*_i_) + _sc.adj;
96 vardef make_element_inputs(suffix $)(expr xlen, ylen) =
98 _make_element_ports($, in, if $.rev: s else: n fi, $.nin, xlen-6, false);
100 _make_element_ports($, in, if $.rev: e else: w fi, $.nin, ylen, false);
104 vardef make_element_outputs(suffix $)(expr xlen, ylen) =
106 _make_element_ports($, out, if $.rev: n else: s fi, $.nout, xlen-6, true);
108 _make_element_ports($, out, if $.rev: w else: e fi, $.nout, ylen, true);
112 vardef clearelement_(suffix $) =
114 generic_redeclare(numeric) _n.down, _n.rev, _n.sidevector, _n.flowvector, _n.width, _n.height, _n.nin, _n.nout, _n.borderscale, _n.portscale, _n.drawports;
115 _n_ := str $ & ".in0";
116 generic_redeclare(numeric) _n;
117 _n_ := str $ & ".out0";
118 generic_redeclare(numeric) _n;
119 _n_ := str $ & ".inpers0";
120 generic_redeclare(numeric) _n;
121 _n_ := str $ & ".outpers0";
122 generic_redeclare(numeric) _n;
123 _n_ := "elemdraw_." & str $;
124 generic_redeclare(numeric) _n;
127 vardef _elementit@#(expr label_str, ninputs, noutputs, personality, down_var, rev_var) =
128 picture _label_; numeric _x_, _y_;
130 if picture label_str: _label_ = label_str
131 elseif label_str = "": _label_ = nullpicture
132 else: _label_ = label_str infont defaultelementfont scaled defaultscale
137 generic_declare(boolean) _n.down, _n.rev, _n.drawports;
138 generic_declare(pair) _n.sidevector, _n.flowvector;
139 generic_declare(numeric) _n.width, _n.height, _n.nin, _n.nout, _n.borderscale, _n.portscale;
140 _n_ := str @# & ".in0";
141 generic_declare(pair) _n;
142 _n_ := str @# & ".out0";
143 generic_declare(pair) _n;
144 _n_ := "elemdraw_." & str @#;
145 generic_declare(string) _n;
148 if down_var: @#.sidevector = (-1, 0); else: @#.sidevector = (0, 1); fi;
149 if down_var: @#.flowvector = (0, -1); else: @#.flowvector = (1, 0); fi;
151 if rev_var: @#.flowvector := -@#.flowvector; @#.sidevector := -@#.sidevector; fi;
154 @#.width = xpart(@#.e - @#.w);
155 @#.height = ypart(@#.n - @#.s);
159 if ninputs > 0: make_element_inputs(@#, @#.width, @#.height); fi;
160 if noutputs > 0: make_element_outputs(@#, @#.width, @#.height); fi;
163 if _x_ = push_to_pull: _x_ := push;
164 elseif _x_ = pull_to_push: _x_ := pull; fi;
165 for _y_ = 0 upto ninputs-1: @#.inpers[_y_] = _x_; endfor;
168 if _x_ = push_to_pull: _x_ := pull;
169 elseif _x_ = pull_to_push: _x_ := push; fi;
170 for _y_ = 0 upto noutputs-1: @#.outpers[_y_] = _x_; endfor;
172 elemdraw_@# = "drawboxes";
173 sproc_@# := "sizeelement_";
175 expandafter def expandafter clearboxes expandafter =
176 clearboxes clearelement_(@#);
180 vardef elementit@#(expr s, ninputs, noutputs, personality_var) =
181 _elementit.@#(s, ninputs, noutputs, personality_var, false, false);
183 vardef relementit@#(expr s, ninputs, noutputs, personality_var) =
184 _elementit.@#(s, ninputs, noutputs, personality_var, false, true);
186 vardef velementit@#(expr s, ninputs, noutputs, personality_var) =
187 _elementit.@#(s, ninputs, noutputs, personality_var, true, false);
189 vardef rvelementit@#(expr s, ninputs, noutputs, personality_var) =
190 _elementit.@#(s, ninputs, noutputs, personality_var, true, true);
196 vardef killinput(suffix $)(expr p) =
197 if (p >= 0) and (p < $.nin): save _i_;
198 for _i_ = p upto $.nin-2:
199 $.in[_i_] := $.in[_i_+1];
200 $.inpers[_i_] := $.inpers[_i_+1];
206 vardef killoutput(suffix $)(expr p) =
207 if (p >= 0) and (p < $.nout): save _i_;
208 for _i_ = p upto $.nout-2:
209 $.out[_i_] := $.out[_i_+1];
210 $.outpers[_i_] := $.outpers[_i_+1];
216 vardef portinteriorin(suffix $)(expr i) =
218 _p_ := if $.inpers[i] >= agnostic: _agnostic_input else: _normal_input fi
220 if $.down: _p_ := _p_ rotated -90 fi;
221 if $.rev: _p_ := _p_ rotated 180 fi;
222 _p_ := _p_ shifted $.in[i];
223 if $.down and $.rev: .5[ulcorner _p_,urcorner _p_]
224 elseif $.down: .5[llcorner _p_,lrcorner _p_]
225 elseif $.rev: .5[ulcorner _p_,llcorner _p_]
226 else: .5[urcorner _p_,lrcorner _p_] fi
229 vardef portinteriorout(suffix $)(expr i) =
231 _p_ := if $.outpers[i] >= agnostic: _agnostic_output else: _normal_output fi
233 if $.down: _p_ := _p_ rotated -90 fi;
234 if $.rev: _p_ := _p_ rotated 180 fi;
235 _p_ := _p_ shifted $.out[i];
236 if $.down and $.rev: .5[llcorner _p_,lrcorner _p_]
237 elseif $.down: .5[ulcorner _p_,urcorner _p_]
238 elseif $.rev: .5[urcorner _p_,lrcorner _p_]
239 else: .5[ulcorner _p_,llcorner _p_] fi
245 vardef set_element_dx(suffix $) =
247 x.maxport = max($.nin, $.nout);
248 x.len = x.maxport*port_length + (x.maxport-1)*port_sep + 2port_offset;
249 x.w = xpart(urcorner pic_$ - llcorner pic_$);
250 x.ww = x.w + 2xpart(element_offset);
251 if x.len > x.ww: $.dx = (x.len - x.w) / 2;
252 else: $.dx = xpart element_offset; fi;
254 $.dx = xpart element_offset;
258 vardef set_element_dy(suffix $) =
260 y.h = ypart(urcorner pic_$ - llcorner pic_$);
261 y.hh = y.h + 2ypart(element_offset);
262 if $.down: y := y.hh;
264 y.maxport = max($.nin, $.nout);
265 y.len = y.maxport*port_length + (y.maxport-1)*port_sep + 2port_offset;
266 y := max(y.hh, y.len);
269 y'' := min_element_height;
272 y'' := y'' + element_height_increment;
275 $.dy = (y'' - y.h)/2;
278 def sizeelement_(suffix $) =
279 if unknown $.dx: set_element_dx($); fi
280 if unknown $.dy: set_element_dy($); fi
281 if unknown $.borderscale: $.borderscale = defaultelementborderscale; fi
282 if unknown $.portscale: $.portscale = defaultelementportscale; fi
285 vardef fixelementsizeleft(text t) =
288 if $.dx > xpart element_offset: $.off := $.off - ($.dx - xpart element_offset, 0); fi;
292 vardef fixelement(text elements) =
297 vardef elementleftjustify(text elements) =
299 forsuffixes $=elements:
300 if $.dx > xpart element_offset: $.off := $.off - ($.dx - xpart element_offset, 0); fi;
304 vardef fixrelations(suffix efirst)(text elements) =
305 forsuffixes $=elements:
306 if unknown xpart(efirst.off - $.off): xpart $.off = xpart efirst.off fi;
307 if unknown ypart(efirst.off - $.off): ypart $.off = ypart efirst.off fi;
311 vardef elementbbox(suffix efirst)(text elements) =
312 fixsize(efirst,elements);
313 fixrelations(efirst,elements);
314 save __t,__l,__r,__b,__p; picture __p;
315 __t = ypart(efirst.n - efirst.off);
316 __l = xpart(efirst.w - efirst.off);
317 __r = xpart(efirst.e - efirst.off);
318 __b = ypart(efirst.s - efirst.off);
319 forsuffixes $=elements:
320 if ypart($.n - efirst.off) > __t: __t := ypart($.n - efirst.off) fi;
321 if xpart($.w - efirst.off) < __l: __l := xpart($.w - efirst.off) fi;
322 if xpart($.e - efirst.off) > __r: __r := xpart($.e - efirst.off) fi;
323 if ypart($.s - efirst.off) < __b: __b := ypart($.s - efirst.off) fi;
326 setbounds __p to ((__l,__t) -- (__r,__t) -- (__r,__b) -- (__l,__b) -- cycle)
327 if known efirst.off: shifted efirst.off fi;
331 vardef compoundelementlink@#(suffix efirst)(text elements) =
332 fixsize(efirst,elements);
333 fixrelations(efirst,elements);
334 save __t,__l,__r,__b,__p; picture __p;
335 __t = ypart(efirst.n - efirst.off);
336 __l = xpart(efirst.w - efirst.off);
337 __r = xpart(efirst.e - efirst.off);
338 __b = ypart(efirst.s - efirst.off);
339 forsuffixes $=elements:
340 if ypart($.n - efirst.off) > __t: __t := ypart($.n - efirst.off) fi;
341 if xpart($.w - efirst.off) < __l: __l := xpart($.w - efirst.off) fi;
342 if xpart($.e - efirst.off) > __r: __r := xpart($.e - efirst.off) fi;
343 if ypart($.s - efirst.off) < __b: __b := ypart($.s - efirst.off) fi;
345 @#.c = efirst.off + .5[(__l,__t), (__r,__b)]
351 vardef draw_element_inputs(suffix $) =
353 _p_ := _normal_input scaled $.portscale;
354 _ag_ := _agnostic_input scaled $.portscale;
355 if $.down: _p_ := _p_ rotated -90; _ag_ := _ag_ rotated -90; fi;
356 if $.rev: _p_ := _p_ rotated 180; _ag_ := _ag_ rotated 180; fi;
357 for _i_ = 0 upto $.nin - 1:
358 if $.inpers[_i_] >= 0:
359 fill _p_ shifted $.in[_i_] withcolor personalitycolor[$.inpers[_i_]];
360 draw _p_ shifted $.in[_i_];
361 if $.inpers[_i_] >= agnostic:
362 fill _ag_ shifted $.in[_i_] withcolor agnosticcolor[$.inpers[_i_]];
363 draw _ag_ shifted $.in[_i_]; fi
368 vardef draw_element_outputs(suffix $) =
370 _p_ := _normal_output scaled $.portscale;
371 _ag_ := _agnostic_output scaled $.portscale;
372 if $.down: _p_ := _p_ rotated -90; _ag_ := _ag_ rotated -90; fi;
373 if $.rev: _p_ := _p_ rotated 180; _ag_ := _ag_ rotated 180; fi;
374 for _i_ = 0 upto $.nout - 1:
375 if $.outpers[_i_] >= 0:
376 fill _p_ shifted $.out[_i_] withcolor personalitycolor[$.outpers[_i_]];
377 draw _p_ shifted $.out[_i_];
378 if $.outpers[_i_] >= agnostic:
379 fill _ag_ shifted $.out[_i_] withcolor agnosticcolor[$.outpers[_i_]];
380 draw _ag_ shifted $.out[_i_]; fi
385 vardef drawelement(text elements) text rest =
386 drawelementbox(elements) rest;
387 drawunboxed(elements);
390 vardef drawelementbox(text elements) text rest =
391 save $, oldpen; oldpen := savepen;
392 interim linejoin := mitered;
395 forsuffixes $ = elements:
397 pickup elementpen.port scaled $.portscale;
398 if $.nin > 0: draw_element_inputs($); fi;
399 if $.nout > 0: draw_element_outputs($); fi;
401 if $.borderscale > 0:
402 pickup elementpen.border scaled $.borderscale;
403 scantokens elemdraw_$($) rest;
409 vardef fillelement(text elements)(text color) =
412 forsuffixes $=elements:
413 fill bpath.$ withcolor color;
420 vardef _drawqueued(expr p,delta,rot,lim,pp) text rest =
421 save i; interim linecap := squared; i := delta;
423 draw (p) shifted ((i,0) rotated rot) withpen currentpen scaled 0.25 rest;
424 i := i + delta; exitunless i < lim;
428 def drawqueued(suffix $) =
429 _drawqueued($.ne -- $.se, 6, 180, .9*$.width, $.nw -- $.ne -- $.se -- $.sw)
431 def drawrqueued(suffix $) =
432 _drawqueued($.nw -- $.sw, 6, 0, .9*$.width, $.ne -- $.nw -- $.sw -- $.se)
434 def drawvqueued(suffix $) =
435 _drawqueued($.se -- $.sw, 5, 90, .9*$.height, $.nw -- $.sw -- $.se -- $.ne)
437 def drawrvqueued(suffix $) =
438 _drawqueued($.ne -- $.nw, 5, 270, .9*$.height, $.sw -- $.nw -- $.ne -- $.se)
441 vardef queueit@#(expr s) =
442 _elementit.@#(s, 1, 1, push_to_pull, false, false);
443 elemdraw_@# := "drawqueued";
445 vardef rqueueit@#(expr s) =
446 _elementit.@#(s, 1, 1, push_to_pull, false, true);
447 elemdraw_@# := "drawrqueued";
449 vardef vqueueit@#(expr s) =
450 _elementit.@#(s, 1, 1, push_to_pull, true, false);
451 elemdraw_@# := "drawvqueued";
453 vardef rvqueueit@#(expr s) =
454 _elementit.@#(s, 1, 1, push_to_pull, true, true);
455 elemdraw_@# := "drawrvqueued";
463 vardef arrowhead expr p =
464 save q,h,e,f; path q,h; pair e,f;
465 e = point length p of p;
466 q = gobble(p shifted -e cutafter makepath(pencircle scaled 2ahlength))
468 h = gobble(p shifted -e cutafter makepath(pencircle scaled 1.5ahlength))
471 (q rotated .5ahangle & reverse q rotated -.5ahangle -- f -- cycle) shifted e
473 def _cutarr(expr b,e) text t =
474 _cutarrpic := image(draw (0,0) -- (1,0) -- cycle t);
475 _cutarramt := (2ypart urcorner _cutarrpic / sind .5ahangle) - 0.75;
477 _apth := subpath (xpart(_apth intersectiontimes makepath(pencircle
478 scaled (b*_cutarramt)) shifted (point 0 of _apth)),
479 xpart(_apth intersectiontimes makepath(pencircle scaled (e*_cutarramt))
480 shifted (point length _apth of _apth))) of _apth;
485 draw (subpath (0, xpart(_apth intersectiontimes makepath(pencircle scaled 1.2ahlength) shifted (point length _apth of _apth))) of _apth) t;
486 fill arrowhead _apth t
488 def _findarr text t =
491 (xpart(_apth intersectiontimes makepath(pencircle scaled 1.2ahlength) shifted (point 0 of _apth)),
492 xpart(_apth intersectiontimes makepath(pencircle scaled 1.2ahlength) shifted (point length _apth of _apth))) of _apth) t;
493 fill arrowhead _apth t;
494 fill arrowhead reverse _apth t
497 def connectpath(suffix $,#,##,$$) =
498 $.out[#]{$.flowvector} .. {$$.flowvector}$$.in[##]
500 vardef drawconnectj(suffix $,#,##,$$)(text t) text rest =
501 interim linejoin := mitered;
502 drawarrow $.out[#]{$.flowvector} t {$$.flowvector}$$.in[##] withpen connectionpen rest
504 def drawconnect(suffix $,#,##,$$) =
505 drawconnectj($,#,##,$$)(..)
507 vardef drawconnectna(suffix $,#,##,$$) text rest =
508 interim linejoin := mitered;
509 draw $.out[#]{$.flowvector} .. {$$.flowvector}$$.in[##] withpen connectionpen rest
512 def drawconnarrow expr p =
513 _apth:=p; _finarr withpen connectionpen
515 def drawconnarrowna expr p =
516 draw p withpen connectionpen
518 def drawdblconnarrow expr p =
519 _apth:=p; _findarr withpen connectionpen