%D \module
%D   [       file=mp-char.mpiv,
%D        version=2011.10.1, % 1998.10.10,
%D          title=\CONTEXT\ \METAPOST\ graphics,
%D       subtitle=charts,
%D         author=Hans Hagen,
%D           date=\currentdate,
%D      copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
%C
%C This module is part of the \CONTEXT\ macro||package and is
%C therefore copyrighted by \PRAGMA. See licen-en.pdf for
%C details.

%D This is ancient code .. but I see no need to rewrite it. This is
%D already a partial rewrite but more could be delegated to \LUA\
%D when used in \CONTEXT\ but it does not pay off now to look into
%D that.

%D For historic reason we first build and then flush but we could
%D as well flush directly which would save us caching.

if unknown context_shap : input "mp-shap.mpiv" ; fi ;
if   known context_flow : endinput ; fi ;

boolean context_flow ; context_flow := true ;

%D settings

numeric flow_grid_width             ; flow_grid_width             := 60pt ;
numeric flow_shape_width            ; flow_shape_width            := 45pt ;
numeric flow_grid_height            ; flow_grid_height            := 40pt ;
numeric flow_shape_height           ; flow_shape_height           := 30pt ;
numeric flow_chart_offset           ; flow_chart_offset           :=  2pt ;
numeric flow_chart_clip_offset      ; flow_chart_offset           := 10pt ;
numeric flow_chart_left_offset      ; flow_chart_offset           :=  0 ;
numeric flow_chart_right_offset     ; flow_chart_offset           :=  0 ;
numeric flow_chart_top_offset       ; flow_chart_offset           :=  0 ;
numeric flow_chart_bottom_offset    ; flow_chart_offset           :=  0 ;
string  flow_chart_background_color ; flow_chart_background_color := "white" ;
boolean flow_show_mid_points        ; flow_show_mid_points        := false ;
boolean flow_show_con_points        ; flow_show_con_points        := false ;
boolean flow_show_all_points        ; flow_show_all_points        := false ;
numeric flow_shape_line_width       ; flow_shape_line_width       := 2pt ;
string  flow_shape_line_color       ; flow_shape_line_color       := "gray-5" ;
string  flow_shape_fill_color       ; flow_shape_fill_color       := "gray-9";
string  flow_connection_line_color  ; flow_connection_line_color  := "gray-2" ;

numeric flow_connection_line_width  ; flow_connection_line_width  := flow_shape_line_width ;

numeric flow_connection_smooth_size ; flow_connection_smooth_size := 5pt ;
numeric flow_connection_arrow_size  ; flow_connection_arrow_size  := 4pt ;
numeric flow_connection_dash_size   ; flow_connection_dash_size   := 3pt ;

numeric flow_max_x                  ; flow_max_x                  := 6 ;
numeric flow_max_y                  ; flow_max_y                  := 4 ;

boolean flow_smooth                 ; flow_smooth                 := true ;
boolean flow_peepshape              ; flow_peepshape              := false ;
boolean flow_arrowtip               ; flow_arrowtip               := true ;
boolean flow_dashline               ; flow_dashline               := false ;
boolean flow_forcevalid             ; flow_forcevalid             := false ;
boolean flow_touchshape             ; flow_touchshape             := false ;
boolean flow_showcrossing           ; flow_showcrossing           := false ;
boolean flow_reverse_y              ; flow_reverse_y              := true ;

picture flow_dash_pattern           ; flow_dash_pattern           := nullpicture ;

numeric flow_shape_node             ; flow_shape_node             :=  0 ;
numeric flow_shape_action           ; flow_shape_action           := 24 ;
numeric flow_shape_procedure        ; flow_shape_procedure        :=  5 ;
numeric flow_shape_product          ; flow_shape_product          := 12 ;
numeric flow_shape_decision         ; flow_shape_decision         := 14 ;
numeric flow_shape_archive          ; flow_shape_archive          := 19 ;
numeric flow_shape_loop             ; flow_shape_loop             := 35 ;
numeric flow_shape_wait             ; flow_shape_wait             :=  6 ;
numeric flow_shape_subprocedure     ; flow_shape_subprocedure     := 20 ;
numeric flow_shape_singledocument   ; flow_shape_singledocument   := 32 ;
numeric flow_shape_multidocument    ; flow_shape_multidocument    := 33 ;
numeric flow_shape_right            ; flow_shape_right            := 66 ;
numeric flow_shape_left             ; flow_shape_left             := 67 ;
numeric flow_shape_up               ; flow_shape_up               := 68 ;
numeric flow_shape_down             ; flow_shape_down             := 69 ;

numeric flow_label_offset           ; flow_label_offset           := 0 ;
numeric flow_exit_offset            ; flow_exit_offset            := 0 ;
numeric flow_comment_offset         ; flow_comment_offset         := 0 ;

% vardef some_shape_path (expr type) == imported from mp-shap

def flow_show_shapes(expr n) =
    flow_begin_chart(n,8,10) ;
    flow_show_con_points := true ;
    for i=0 upto 7 :
        for j=0 upto 9 :
            flow_new_shape(i+1,j+1,i*10+j);
        endfor ;
    endfor ;
    flow_end_chart ;
enddef ;

%D connections

def flow_new_chart =

    flow_grid_width             := 60pt ;
    flow_shape_width            := 45pt ;
    flow_grid_height            := 40pt ;
    flow_shape_height           := 30pt ;
    flow_chart_offset           :=  2pt ;
    flow_chart_clip_offset      := 10pt ;
    flow_chart_left_offset      :=  0 ;
    flow_chart_right_offset     :=  0 ;
    flow_chart_top_offset       :=  0 ;
    flow_chart_bottom_offset    :=  0 ;
    flow_chart_background_color := "white" ;
    flow_show_mid_points        := false ;
    flow_show_con_points        := false ;
    flow_show_all_points        := false ;
    flow_shape_line_width       := 2pt ;
    flow_shape_line_color       := "gray-5" ;
    flow_shape_fill_color       := "gray-9" ;
    flow_connection_line_color  := "gray-2" ;
    flow_connection_line_width  := flow_shape_line_width ;
    flow_connection_smooth_size := 5pt ;
    flow_connection_arrow_size  := 4pt ;
    flow_connection_dash_size   := 3pt ;
    flow_label_offset           := 0 ;
    flow_exit_offset            := 0 ;
    flow_comment_offset         := 0 ;

    flow_max_x                  := 6 ;
    flow_max_y                  := 4 ;

    flow_smooth                 := true  ;
    flow_peepshape              := false ;
    flow_arrowtip               := true  ;
    flow_dashline               := false ;
    flow_forcevalid             := false ;
    flow_touchshape             := false ;
    flow_showcrossing           := false ;
    flow_reverse_y              := true ;

    flow_dash_pattern           := nullpicture ;

    numeric flow_xypoint   ; flow_xypoint   := 0 ;
    numeric flow_cpath     ; flow_cpath     := 0 ;

    pair    flow_xypoints   []   ;
    boolean flow_xyfree     [][] ;
    path    flow_xypath     [][] ;
    numeric flow_xysx       [][] ;
    numeric flow_xysy       [][] ;
    string  flow_xyfill     [][] ;
    string  flow_xydraw     [][] ;
    numeric flow_xyline     [][] ;
    boolean flow_xypeep     [][] ;
    picture flow_xytext     [][] ;
    picture flow_xylabel    [][] ;
    picture flow_xyexit     [][] ;
    picture flow_xycomment  [][] ;
    path    flow_cpaths     [] ;
    numeric flow_cline      [] ;
    string  flow_ccolor     [] ;
    boolean flow_carrow     [] ;
    boolean flow_cdash      [] ;
    boolean flow_ccross     [] ;
    picture flow_tpicture   [][] ;
    picture flow_bpicture   [][] ;
    picture flow_lpicture   [][] ;
    picture flow_rpicture   [][] ;
    path    flow_connections[][][] ;

    predefined_shapes[61] := (fullcircle scaled (1.5*predefined_shapes_yradius) xscaled (flow_grid_height/flow_grid_width)) ;
    predefined_shapes[62] := (fullcircle scaled (2.0*predefined_shapes_yradius) xscaled (flow_grid_height/flow_grid_width)) ;

enddef ;

flow_new_chart ;

def flow_y_pos(expr y) =
%     if flow_reverse_y :
        flow_max_y + 1 - y
%     else :
%         y
%     fi
enddef ;

def flow_initialize_grid(expr maxx, maxy) =
    flow_max_x := maxx ;
    flow_max_y := maxy ;
    flow_dsp_x := 0 ;
    flow_dsp_y := 0 ;
    for x=1 upto flow_max_x :
        for y=1 upto flow_max_y :
            flow_xyfree[x][y] := true ;
            flow_xyfill[x][y] := flow_shape_fill_color ;
            flow_xydraw[x][y] := flow_shape_line_color ;
            flow_xyline[x][y] := flow_shape_line_width ;
        endfor ;
    endfor ;
enddef ;

def flow_scaled_to_grid =
    xscaled flow_grid_width yscaled flow_grid_height
enddef ;

def flow_xy_offset(expr x, y) =
    (x+.5,y+.5)
enddef ;

def flow_draw_shape(expr x, yy, p, sx, sy) =
    begingroup ;
        save y ; numeric y ;
        y := flow_y_pos(yy) ;
        flow_xypath [x][y] := (p xscaled sx yscaled sy) shifted flow_xy_offset(x,y) ;
        flow_xyfree [x][y] := false ;
        flow_xysx   [x][y] := sx ;
        flow_xysy   [x][y] := sy ;
        flow_xyfill [x][y] := flow_shape_fill_color ;
        flow_xydraw [x][y] := flow_shape_line_color ;
        flow_xyline [x][y] := flow_shape_line_width ;
        flow_xypeep [x][y] := flow_peepshape ;
    endgroup ;
enddef ;

vardef flow_i_point (expr x, y, p, t) =
    begingroup ;
        save q, ok ; pair q ; boolean ok ;
        q := flow_xypath[x][y] intersectionpoint ((p) shifted flow_xy_offset(x,y)) ;
        ok := true ;
        if not ok :
            message (t & " of shape (" & decimal x & "," & decimal y & ") limited") ;
        fi ;
        q
    endgroup
enddef ;

vardef flow_trimmed (expr x, y, z, t) =
    if flow_touchshape and t :
        flow_xyline[x][y]/z
    else :
        epsilon
    fi
enddef ;

numeric flow_zfactor ; flow_zfactor := 1/3 ;

vardef flow_xy_bottom (expr x, y, z, t) =
    flow_i_point(x, y, ((0,0)--(0,-2)) shifted (flow_zfactor*z*flow_xysx[x][y],0), "bottom")
    shifted(0,-flow_trimmed(x,y,flow_grid_height,t))
enddef ;

vardef flow_xy_top (expr x, y, z, t) =
    flow_i_point (x, y, ((0,0)--(0,2))  shifted (flow_zfactor*z*flow_xysx[x][y],0), "top")
    shifted(0,flow_trimmed(x,y,flow_grid_height,t))
enddef ;

vardef flow_xy_left (expr x, y, z, t) =
    flow_i_point (x, y, ((0,0)--(-2,0)) shifted (0,flow_zfactor*z*flow_xysy[x][y]), "left")
    shifted(-flow_trimmed(x,y,flow_grid_width,t),0)
enddef ;

vardef flow_xy_right (expr x, y, z, t) =
    flow_i_point (x, y, ((0,0)--(2,0))  shifted (0,flow_zfactor*z*flow_xysy[x][y]), "right")
    shifted(flow_trimmed(x,y,flow_grid_width,t),0)
enddef ;

def flow_flush_shapes =
    for x=1 upto flow_max_x :
        for y=1 upto flow_max_y :
            flow_flush_shape(x, y) ;
        endfor ;
    endfor ;
enddef ;

def flow_flush_pictures =
    for x=1 upto flow_max_x :
        for y=1 upto flow_max_y :
            flow_flush_picture(x, y) ;
        endfor ;
    endfor ;
enddef ;

def flow_draw_connection_point(expr x, y, z) =
    pickup pencircle scaled if (z=0): 2 fi flow_xyline[x][y] ;
    drawdot flow_xy_bottom(x,y,z,false) flow_scaled_to_grid withcolor (1,0,0) ;
    drawdot flow_xy_top   (x,y,z,false) flow_scaled_to_grid withcolor (0,1,0) ;
    drawdot flow_xy_left  (x,y,z,false) flow_scaled_to_grid withcolor (0,0,1) ;
    drawdot flow_xy_right (x,y,z,false) flow_scaled_to_grid withcolor (1,1,0) ;
enddef ;

def flow_flush_shape(expr x, yy) =
    begingroup ;
        save y ; numeric y ;
        y := flow_y_pos(yy) ;
        if not flow_xyfree[x][y] :
            pickup pencircle scaled flow_xyline[x][y] ;
            if flow_xypeep[x][y] :
                fill (flow_xypath[x][y] peepholed (unitsquare shifted (x,y)))
                    flow_scaled_to_grid withpen pencircle scaled 0
                    withcolor flow_chart_background_color ;
            else :
                fill flow_xypath[x][y] flow_scaled_to_grid withcolor flow_xyfill[x][y] ;
            fi ;
            draw flow_xypath[x][y] flow_scaled_to_grid withcolor flow_xydraw[x][y] ;
            if flow_show_con_points or flow_show_all_points :
                flow_draw_connection_point(x, y, 0) ;
            fi ;
            if flow_show_all_points :
                for i=-1 upto 1 :
                    flow_draw_connection_point(x, y, i) ;
                endfor ;
            fi ;
        fi ;
    endgroup ;
enddef ;

vardef flow_points_initialized(expr xfrom, yfrom, xto, yto, n) =
    if unknown flow_xyfree[xfrom][yfrom] or unknown flow_xyfree[xto][yto] :
        flow_xypoint := 0 ; false
    elseif not flow_xyfree[xfrom][yfrom] and not flow_xyfree[xto][yto] :
        flow_xypoint := n ; true
    else :
        flow_xypoint := 0 ; false
    fi
enddef ;

def flow_collapse_points = % this can become a core macro
    begingroup ;
        % remove redundant points
        save n ; numeric n ;
        n := 1 ;
        for i=2 upto flow_xypoint :
% message(flow_xypoints[i]);
% message(flow_xypoints[n]);
          % if not (flow_xypoints[i] = flow_xypoints[n]) :
            if abs(flow_xypoints[i] - flow_xypoints[n]) > eps :
                n := n + 1 ;
                flow_xypoints[n] := flow_xypoints[i]
            fi ;
        endfor ;
        flow_xypoint := n ;
        % make straight lines
% message(flow_xypoints[2]);
% message(flow_xypoints[flow_xypoint-1]);
      % if flow_xypoints[2] = flow_xypoints[flow_xypoint-1] :
        if abs(flow_xypoints[2] - flow_xypoints[flow_xypoint-1]) < eps :
            flow_xypoints[3] := flow_xypoints[flow_xypoint] ;
            flow_xypoint := 3 ;
        fi ;
    endgroup ;
enddef ;

vardef flow_smooth_connection(expr a,b) =
    if ypart a = ypart b :
        a shifted (  if xpart a >= xpart b : - fi (flow_connection_smooth_size/flow_grid_width ),0)
    else :
        a shifted (0,if ypart a >= ypart b : - fi (flow_connection_smooth_size/flow_grid_height)  )
    fi
enddef ;

% vardef flow_trim_points =
%     begingroup
%         save p, a, b, d, i ; numeric a, b ; path p ; pair d ;
%         p := for i=1 upto flow_xypoint-1 : flow_xypoints[i]-- endfor flow_xypoints[flow_xypoint] ;
%         if flow_touchshape :
%             a := flow_shape_line_width/flow_grid_width ;
%             b := flow_shape_line_width/flow_grid_height ;
%         else :
%             a := epsilon ;
%             b := epsilon ;
%         fi ;
%         d := direction infinity of p ;
%         flow_xypoints[flow_xypoint] := flow_xypoints[flow_xypoint] shifted
%         if     xpart d < 0 : (+a,0) ;
%         elseif xpart d > 0 : (-a,0) ;
%         elseif ypart d < 0 : (0,+b) ;
%         elseif ypart d > 0 : (0,-b) ;
%         else               : origin ;
%         fi ;
%         d := direction 0 of p ;
%         flow_xypoints[1] := flow_xypoints[1] shifted
%         if     xpart d < 0 : (-a,0) ;
%         elseif xpart d > 0 : (+a,0) ;
%         elseif ypart d < 0 : (0,-b) ;
%         elseif ypart d > 0 : (0,+b) ;
%         else               : origin ;
%         fi ;
%     endgroup
% enddef ;

vardef flow_trim_points = enddef ;

vardef flow_connection_path =
    if flow_reverse_connection : reverse fi (flow_xypoints[1] --
    for i=2 upto flow_xypoint-1 :
        if flow_smooth :
            flow_smooth_connection(flow_xypoints[i],flow_xypoints[i-1]) ..
            controls flow_xypoints[i] and flow_xypoints[i] ..
            flow_smooth_connection(flow_xypoints[i],flow_xypoints[i+1]) --
        else :
            flow_xypoints[i] --
        fi
    endfor
    flow_xypoints[flow_xypoint])
enddef ;

def flow_draw_connection(expr i,xfrom,yfrom,xto,yto) = % 'i' is a comment reference
    if flow_xypoint > 0 :
        flow_collapse_points ;
        flow_trim_points ;
        flow_cpath := flow_cpath + 1 ; % maybe also store as x,y
        flow_cpaths[flow_cpath] := flow_connection_path flow_scaled_to_grid ;
        flow_cline[flow_cpath]  := flow_connection_line_width ;
        flow_ccolor[flow_cpath] := flow_connection_line_color ;
        flow_carrow[flow_cpath] := flow_arrowtip ;
        flow_cdash[flow_cpath]  := flow_dashline ;
        flow_ccross[flow_cpath] := flow_showcrossing ;
        if flow_reverse_connection :
            flow_connections[xto]  [yto]  [i] := flow_cpaths[flow_cpath] ;
        else :
            flow_connections[xfrom][yfrom][i] := flow_cpaths[flow_cpath] ;
        fi ;
    else :
        message("no connection defined") ;
    fi ;
    flow_reverse_connection := false ;
enddef ;

def flow_flush_connections = % protect locals
    begingroup ;
    save ip, crossing, cp ; numeric ip ; boolean crossing ; path cp ;
    ahlength := flow_connection_arrow_size ;
    flow_dash_pattern := dashpattern(on flow_connection_dash_size off flow_connection_dash_size) ;
    for i=1 upto flow_cpath :
        if flow_ccross[i] :
            crossing := false ;
            for j=1 upto i :
                if not (point infinity of flow_cpaths[i] = point infinity of flow_cpaths[j]) :
                    ip := flow_cpaths[i] intersection_point flow_cpaths[j] ;
                    if intersection_found : crossing := true fi ;
                fi ;
            endfor ;
            if crossing :
                pickup pencircle scaled 2flow_cline[i] ;
                cp := flow_cpaths[i] ;
                cp := cp cutbefore point .05 length cp of cp ;
                cp := cp cutafter  point .95 length cp of cp ;
                draw cp withcolor flow_chart_background_color ;
            fi ;
        fi ;
        pickup pencircle scaled flow_cline[i] ;
        if flow_carrow[i] :
            if flow_cdash[i] :
                drawarrow flow_cpaths[i] withcolor flow_ccolor[i] dashed flow_dash_pattern ;
            else :
                drawarrow flow_cpaths[i] withcolor flow_ccolor[i] ;
            fi ;
        else :
            if flow_cdash[i] :
                draw flow_cpaths[i] withcolor flow_ccolor[i] dashed flow_dash_pattern ;
            else :
                draw flow_cpaths[i] withcolor flow_ccolor[i] ;
            fi ;
        fi ;
        flow_draw_midpoint(i) ;
    endfor ;
    endgroup ;
enddef ;

def flow_draw_midpoint (expr n) =
    begingroup
        save p ; pair p ;
        p := point .5*length(flow_cpaths[n]) of flow_cpaths[n];
        pickup pencircle scaled 2flow_cline[n] ;
        if flow_show_mid_points :
            drawdot p withcolor .7white ;
        fi ;
    endgroup ;
enddef ;

def flow_flush_picture(expr x, yy) =
    begingroup ;
    save y ; numeric y ;
    y := flow_y_pos(yy) ; % maybe move this to the makers
    if known flow_xytext[x][y] :
        draw flow_xytext[x][y] ;
    fi ;
    if known flow_xylabel[x][y] :
        draw flow_xylabel[x][y] ;
    fi ;
    if known flow_xyexit[x][y] :
        draw flow_xyexit[x][y] ;
    fi ;
    if known flow_xycomment[x][y] :
        draw flow_xycomment[x][y] ;
    fi ;
    endgroup ;
enddef ;

vardef flow_offset(expr x, y) =
    flow_xy_offset((x+0.5)*flow_grid_width,(flow_max_y-y+1.5)*flow_grid_height)
        shifted (-flow_xyline[x][y]/4,-flow_xyline[x][y]/4) % terrible hack (some compensation)
enddef ;

def flow_chart_draw_text(expr x, y, p) =
    if known flow_xytext[x][y] :
        addto flow_xytext[x][y] also
    else :
        flow_xytext[x][y] :=
    fi
    p shifted flow_offset(x,y) ;
enddef ;

def flow_chart_draw_label (expr x, y, loc, txt) =
    begingroup ;
        save p, s ; path p ; picture s ;
        p := fullsquare xscaled flow_grid_width yscaled flow_grid_height ;
        p := p shifted flow_offset(x,y) ;
        s := txt ;
        setbounds s to boundingbox s enlarged flow_label_offset ;
        if known flow_xylabel[x][y] :
            addto flow_xylabel[x][y] also
        else :
            flow_xylabel[x][y] :=
        fi
        if     loc = "tr" : anchored.llft(s,0.5[ulcorner p,urcorner p]) ;
        elseif loc = "t"  : anchored.bot (s,0.5[ulcorner p,urcorner p]) ;
        elseif loc = "tl" : anchored.lrt (s,0.5[ulcorner p,urcorner p]) ;
        elseif loc = "br" : anchored.ulft(s,0.5[llcorner p,lrcorner p]) ;
        elseif loc = "b"  : anchored.top (s,0.5[llcorner p,lrcorner p]) ;
        elseif loc = "bl" : anchored.urt (s,0.5[llcorner p,lrcorner p]) ;
        elseif loc = "lb" : anchored.urt (s,0.5[ulcorner p,llcorner p]) ;
        elseif loc = "l"  : anchored.rt  (s,0.5[ulcorner p,llcorner p]) ;
        elseif loc = "lt" : anchored.lrt (s,0.5[ulcorner p,llcorner p]) ;
        elseif loc = "rb" : anchored.ulft(s,0.5[urcorner p,lrcorner p]) ;
        elseif loc = "r"  : anchored.lft (s,0.5[urcorner p,lrcorner p]) ;
        elseif loc = "rt" : anchored.llft(s,0.5[urcorner p,lrcorner p]) ;
        else              : anchored     (s,center p) ;
        fi ;
    endgroup ;
enddef ;

def flow_chart_draw_exit (expr x, y, loc, txt) =
    begingroup ;
        save p, s ; path p ; picture s ;
        p := fullsquare xscaled flow_grid_width yscaled flow_grid_height ;
        p := p shifted flow_offset(x,y) ;
        s := txt ;
        setbounds s to boundingbox s enlarged flow_exit_offset ;
        if known flow_xyexit[x][y] :
            addto flow_xyexit[x][y] also
        else :
            flow_xyexit[x][y] :=
        fi
        if     loc = "t" : anchored.top(s,0.5[ulcorner p,urcorner p]) ;
        elseif loc = "b" : anchored.bot(s,0.5[llcorner p,lrcorner p]) ;
        elseif loc = "l" : anchored.lft(s,0.5[ulcorner p,llcorner p]) ;
        elseif loc = "r" : anchored.rt (s,0.5[urcorner p,lrcorner p]) ;
        else             : anchored    (s,center p) ;
        fi ;
    endgroup ;
enddef ;

def flow_chart_draw_comment (expr x, y, i, loc, len, txt) = % per connection
    begingroup ;
        if known flow_connections[x][y][i] :
            save p, q, s ; path p, q ; picture s ;
            p := fullsquare xscaled flow_shape_width yscaled flow_shape_height ;
            p := p shifted flow_offset(x,y) ;
            q := flow_connections[x][y][i] ; % already relocated
            s := txt ;
            setbounds s to boundingbox s enlarged flow_comment_offset ;
            if known flow_xycomment[x][y] :
                addto flow_xycomment[x][y] also
            else :
                flow_xycomment[x][y] :=
            fi
            if     loc = "tr"   : anchored.llft(s,if len = 0 : 0.5[ulcorner p,urcorner p] else : point len along q fi) ;
            elseif loc = "t"    : anchored.bot (s,if len = 0 : 0.5[ulcorner p,urcorner p] else : point len along q fi) ;
            elseif loc = "tl"   : anchored.lrt (s,if len = 0 : 0.5[ulcorner p,urcorner p] else : point len along q fi) ;
            elseif loc = "br"   : anchored.ulft(s,if len = 0 : 0.5[llcorner p,lrcorner p] else : point len along q fi) ;
            elseif loc = "b"    : anchored.top (s,if len = 0 : 0.5[llcorner p,lrcorner p] else : point len along q fi) ;
            elseif loc = "bl"   : anchored.urt (s,if len = 0 : 0.5[llcorner p,lrcorner p] else : point len along q fi) ;
            elseif loc = "lb"   : anchored.urt (s,if len = 0 : 0.5[ulcorner p,llcorner p] else : point len along q fi) ;
            elseif loc = "l"    : anchored.rt  (s,if len = 0 : 0.5[ulcorner p,llcorner p] else : point len along q fi) ;
            elseif loc = "lt"   : anchored.lrt (s,if len = 0 : 0.5[ulcorner p,llcorner p] else : point len along q fi) ;
            elseif loc = "rb"   : anchored.ulft(s,if len = 0 : 0.5[urcorner p,lrcorner p] else : point len along q fi) ;
            elseif loc = "r"    : anchored.lft (s,if len = 0 : 0.5[urcorner p,lrcorner p] else : point len along q fi) ;
            elseif loc = "rt"   : anchored.llft(s,if len = 0 : 0.5[urcorner p,lrcorner p] else : point len along q fi) ;
            elseif loc = "tr:*" : anchored.llft(s,point 0 of q) ;
            elseif loc = "t:*"  : anchored.bot (s,point 0 of q) ;
            elseif loc = "tl:*" : anchored.lrt (s,point 0 of q) ;
            elseif loc = "br:*" : anchored.ulft(s,point 0 of q) ;
            elseif loc = "b:*"  : anchored.top (s,point 0 of q) ;
            elseif loc = "bl:*" : anchored.urt (s,point 0 of q) ;
            elseif loc = "lb:*" : anchored.urt (s,point 0 of q) ;
            elseif loc = "l:*"  : anchored.rt  (s,point 0 of q) ;
            elseif loc = "lt:*" : anchored.lrt (s,point 0 of q) ;
            elseif loc = "rb:*" : anchored.ulft(s,point 0 of q) ;
            elseif loc = "r:*"  : anchored.lft (s,point 0 of q) ;
            elseif loc = "rt:*" : anchored.llft(s,point 0 of q) ;
            else                : anchored     (s,point 0 of q) ;
            fi ;
        fi ;
    endgroup ;
enddef ;

boolean flow_reverse_connection ; flow_reverse_connection := false ;

vardef flow_up_on_grid (expr n) =
    (xpart flow_xypoints[n],(ypart flow_xypoints[n]+1) div 1)
enddef ;

vardef flow_down_on_grid (expr n) =
    (xpart flow_xypoints[n],(ypart flow_xypoints[n]) div 1)
enddef ;

vardef flow_left_on_grid (expr n) =
    ((xpart flow_xypoints[n]) div 1, ypart flow_xypoints[n])
enddef ;

vardef flow_right_on_grid (expr n) =
    ((xpart flow_xypoints[n]+1) div 1, ypart flow_xypoints[n])
enddef ;

vardef flow_x_on_grid (expr n, xfrom, xto, zfrom) =
    if (xfrom = xto) and not (zfrom = 0) :
        if (zfrom=1) : flow_right_on_grid(2) else : flow_left_on_grid(2) fi
    elseif xpart flow_xypoints[1] < xpart flow_xypoints[6] :
        flow_right_on_grid(n)
    else :
        flow_left_on_grid(n)
    fi
enddef ;

vardef flow_y_on_grid (expr n, yfrom, yto, zfrom) =
    if (yfrom = yto) and not (zfrom = 0) :
        if (zfrom = 1) : flow_up_on_grid(2) else : flow_down_on_grid(2) fi
    elseif ypart flow_xypoints[1] < ypart flow_xypoints[6] :
        flow_up_on_grid(n)
    else :
        flow_down_on_grid(n)
    fi
enddef ;

vardef flow_xy_on_grid (expr n, m) =
    (xpart flow_xypoints[n], ypart flow_xypoints[m])
enddef ;

vardef flow_down_to_grid (expr a,b) =
    (xpart flow_xypoints[a], ypart flow_xypoints[if ypart flow_xypoints[a]<ypart flow_xypoints[b] : a else : b fi])
enddef ;

vardef flow_up_to_grid (expr a,b) =
    (xpart flow_xypoints[a], ypart flow_xypoints[if ypart flow_xypoints[a]>ypart flow_xypoints[b] : a else : b fi])
enddef ;

vardef flow_left_to_grid (expr a,b) =
    (xpart flow_xypoints[if xpart flow_xypoints[a]<xpart flow_xypoints[b] : a else : b fi], ypart flow_xypoints[a])
enddef ;

vardef flow_right_to_grid (expr a,b) =
    (xpart flow_xypoints[if xpart flow_xypoints[a]>xpart flow_xypoints[b] : a else : b fi], ypart flow_xypoints[a])
enddef ;

vardef flow_valid_connection (expr xfrom, yfrom, xto, yto) =
    begingroup ;
        save ok, vc, pp ; boolean ok ; pair vc ; path pp ;
        save flow_xyfirst, flow_xylast ; pair flow_xyfirst, flow_xylast ;
        % check for slanted lines
        ok := true ;
        for i=1 upto flow_xypoint-1 :
            if not ((xpart flow_xypoints[i]=xpart flow_xypoints[i+1]) or (ypart flow_xypoints[i]=ypart flow_xypoints[i+1])) :
                ok := false ;
            fi ;
        endfor ;
        if not ok :
            % message("slanted");
            false
        elseif flow_forcevalid :
            % message("force");
            true
        elseif (xfrom=xto) and (yfrom=yto) :
            % message("self");
            false
        else :
            % check for crossing shapes
            flow_xyfirst := flow_xypoints[1] ;
            flow_xylast := flow_xypoints[flow_xypoint] ;
            flow_trim_points ;
            pp := for i=1 upto flow_xypoint-1 : flow_xypoints[i]-- endfor flow_xypoints[flow_xypoint] ;
            flow_xypoints[1] := flow_xyfirst ;
            flow_xypoints[flow_xypoint] := flow_xylast ;
            for i=1 upto flow_max_x :
                for j=1 upto flow_max_y :                % was bug: xfrom,yto
%                     if not ( ( (i,j)=(xfrom,yfrom) ) or ( (i,j)=(xto,yto) ) ) :
                        if not flow_xyfree[i][j] :
                            vc := pp intersection_point flow_xypath[i][j] ;
                            if intersection_found :
                                ok := false
                            fi ;
                        fi ;
%                     fi ;
                endfor ;
            endfor ;
            % if not ok: message("crossing") ; fi ;
            ok
        fi
    endgroup
enddef ;

def flow_connect_top_bottom (expr n) (expr xfrom, yyfrom, zfrom) (expr xto, yyto, zto) =
    begingroup ; save yfrom, yto ;
    yfrom := flow_y_pos(yyfrom) ;
    yto := flow_y_pos(yyto) ;
    if flow_points_initialized(xfrom,yfrom,xto,yto,6) :
        flow_xypoints[1] := flow_xy_top(xfrom,yfrom,zfrom,true) ;
        flow_xypoints[6] := flow_xy_bottom(xto,yto,zto,true) ;
        flow_xypoints[2] := flow_up_on_grid(1) ;
        flow_xypoints[5] := flow_down_on_grid(6) ;
        flow_xypoints[3] := flow_up_to_grid(2,5) ;
        flow_xypoints[4] := flow_up_to_grid(2,5) ;
        if not flow_valid_connection(xfrom,yfrom,xto,yto) :
            flow_xypoints[3] := flow_x_on_grid(2,xfrom,xto,zfrom) ;
            flow_xypoints[4] := flow_xy_on_grid(3,5) ;
        fi ;
        %%%% begin experiment
        flow_xypoints[3] := flow_xypoints[3] shifted (flow_dsp_x,0) ;
        flow_xypoints[4] := flow_xypoints[4] shifted (flow_dsp_x,0) ;
        if flow_dsp_y>0 :
            flow_xypoints[2] := flow_xypoints[2] shifted (0,flow_dsp_y) ;
            flow_xypoints[3] := flow_xypoints[3] shifted (0,flow_dsp_y) ;
        elseif flow_dsp_y<0 :
            flow_xypoints[4] := flow_xypoints[4] shifted (0,flow_dsp_y) ;
            flow_xypoints[5] := flow_xypoints[5] shifted (0,flow_dsp_y) ;
        fi
        %%%% end experiment
        flow_draw_connection(n,xfrom,yyfrom,xto,yyto) ;
    fi ;
    endgroup ;
enddef ;

def flow_connect_left_right (expr n) (expr xfrom,yyfrom,zfrom) (expr xto,yyto,zto) =
    begingroup ; save yfrom, yto ;
    yfrom := flow_y_pos(yyfrom) ;
    yto := flow_y_pos(yyto) ;
    if flow_points_initialized(xfrom,yfrom,xto,yto,6) :
        flow_xypoints[1] := flow_xy_left(xfrom,yfrom,zfrom,true) ;
        flow_xypoints[6] := flow_xy_right(xto,yto,zto,true) ;
        flow_xypoints[2] := flow_left_on_grid(1) ;
        flow_xypoints[5] := flow_right_on_grid(6) ;
        flow_xypoints[3] := flow_left_to_grid(2,5) ;
        flow_xypoints[4] := flow_left_to_grid(2,5) ;
        if not flow_valid_connection(xfrom,yfrom,xto,yto) :
            flow_xypoints[3] := flow_y_on_grid(2,yfrom,yto,zfrom) ;
            flow_xypoints[4] := flow_xy_on_grid(5,3) ;
        fi ;
        %%%% begin experiment
        if flow_dsp_y <> 0 :
            flow_xypoints[3] := flow_xypoints[3] shifted (0,-flow_dsp_y) ;
            flow_xypoints[4] := flow_xypoints[4] shifted (0,-flow_dsp_y) ;
        fi ;
        %%%% end experiment
        flow_draw_connection(n,xfrom,yyfrom,xto,yyto) ;
    fi ;
    endgroup ;
enddef ;

def flow_connect_left_top (expr n) (expr xfrom,yyfrom,zfrom) (expr xto,yyto,zto) =
    begingroup ; save yfrom, yto ;
    yfrom := flow_y_pos(yyfrom) ;
    yto := flow_y_pos(yyto) ;
    if flow_points_initialized(xfrom,yfrom,xto,yto,5) :
        flow_xypoints[1] := flow_xy_left(xfrom,yfrom,zfrom,true) ;
        flow_xypoints[5] := flow_xy_top(xto,yto,zto,true) ;
        flow_xypoints[2] := flow_left_on_grid(1) ;
        flow_xypoints[4] := flow_up_on_grid(5) ;
        flow_xypoints[3] := flow_left_to_grid(2,5) ;
        if not flow_valid_connection(xfrom,yfrom,xto,yto) :
            flow_xypoints[3] := flow_xy_on_grid(2,4) ;
        fi ;
        flow_draw_connection(n,xfrom,yyfrom,xto,yyto) ;
    fi ;
    endgroup ;
enddef ;

def flow_connect_left_bottom (expr n) (expr xfrom,yyfrom,zfrom) (expr xto,yyto,zto) =
    begingroup ; save yfrom, yto ;
    yfrom := flow_y_pos(yyfrom) ;
    yto := flow_y_pos(yyto) ;
    if flow_points_initialized(xfrom,yfrom,xto,yto,5) :
        flow_xypoints[1] := flow_xy_left(xfrom,yfrom,zfrom,true) ;
        flow_xypoints[5] := flow_xy_bottom(xto,yto,zto,true) ;
        flow_xypoints[2] := flow_left_on_grid(1) ;
        flow_xypoints[4] := flow_down_on_grid(5) ;
        flow_xypoints[3] := flow_left_to_grid(2,5) ;
        if not flow_valid_connection(xfrom,yfrom,xto,yto) :
            flow_xypoints[3] := flow_xy_on_grid(2,4) ;
        fi ;
        flow_draw_connection(n,xfrom,yyfrom,xto,yyto) ;
    fi ;
    endgroup
enddef ;

def flow_connect_right_top (expr n) (expr xfrom,yyfrom,zfrom) (expr xto,yyto,zto) =
    begingroup ; save yfrom, yto ;
    yfrom := flow_y_pos(yyfrom) ;
    yto := flow_y_pos(yyto) ;
    if flow_points_initialized(xfrom,yfrom,xto,yto,5) :
        flow_xypoints[1] := flow_xy_right(xfrom,yfrom,zfrom,true) ;
        flow_xypoints[5] := flow_xy_top(xto,yto,zto,true) ;
        flow_xypoints[2] := flow_right_on_grid(1) ;
        flow_xypoints[4] := flow_up_on_grid(5) ;
        flow_xypoints[3] := flow_right_to_grid(2,5) ;
        if not flow_valid_connection(xfrom,yfrom,xto,yto) :
            flow_xypoints[3] := flow_xy_on_grid(2,4) ;
        fi ;
        flow_draw_connection(n,xfrom,yyfrom,xto,yyto) ;
    fi ;
    endgroup
enddef ;

def flow_connect_right_bottom (expr n) (expr xfrom,yyfrom,zfrom) (expr xto,yyto,zto) =
    begingroup ; save yfrom, yto ;
    yfrom := flow_y_pos(yyfrom) ;
    yto := flow_y_pos(yyto) ;
    if flow_points_initialized(xfrom,yfrom,xto,yto,5) :
        flow_xypoints[1] := flow_xy_right(xfrom,yfrom,zfrom,true) ;
        flow_xypoints[5] := flow_xy_bottom(xto,yto,zto,true) ;
        flow_xypoints[2] := flow_right_on_grid(1) ;
        flow_xypoints[4] := flow_down_on_grid(5) ;
        flow_xypoints[3] := flow_right_to_grid(2,5) ;
        if not flow_valid_connection(xfrom,yfrom,xto,yto) :
            flow_xypoints[3] := flow_xy_on_grid(2,4) ;
        fi ;
        %%%% begin experiment
        flow_xypoints[2] := flow_xypoints[2] shifted (flow_dsp_x,0) ;
        flow_xypoints[3] := flow_xypoints[3] shifted (flow_dsp_x,0) ;
        if flow_dsp_y>0 :
          flow_xypoints[3] := flow_xypoints[3] shifted (0,-flow_dsp_y) ;
          flow_xypoints[4] := flow_xypoints[4] shifted (0,-flow_dsp_y) ;
        elseif flow_dsp_y<0 :
          flow_xypoints[3] := flow_xypoints[3] shifted (0,flow_dsp_y) ;
          flow_xypoints[4] := flow_xypoints[4] shifted (0,flow_dsp_y) ;
        fi
        %%%% end experiment
        flow_draw_connection(n,xfrom,yyfrom,xto,yyto) ;
    fi ;
    endgroup
enddef ;

def flow_connect_left_left (expr n) (expr xfrom,yyfrom,zfrom) (expr xto,yyto,zto) =
    begingroup ; save yfrom, yto ;
    yfrom := flow_y_pos(yyfrom) ;
    yto := flow_y_pos(yyto) ;
    if flow_points_initialized(xfrom,yfrom,xto,yto,6) :
        flow_xypoints[1] := flow_xy_left(xfrom,yfrom,zfrom,true) ;
        flow_xypoints[6] := flow_xy_left(xto,yto,zto,true) ;
        flow_xypoints[2] := flow_left_on_grid(1) ;
        flow_xypoints[5] := flow_left_on_grid(6) ;
        flow_xypoints[3] := flow_left_to_grid(2,5) ;
        flow_xypoints[4] := flow_left_to_grid(5,2) ;
        if not flow_valid_connection(xfrom,yfrom,xto,yto) :
            flow_xypoints[3] := flow_y_on_grid(2,yfrom,yto,zfrom) ;
            flow_xypoints[4] := flow_xy_on_grid(5,3) ;
        fi ;
        flow_draw_connection(n,xfrom,yyfrom,xto,yyto) ;
    fi ;
    endgroup
enddef ;

def flow_connect_right_right (expr n) (expr xfrom,yyfrom,zfrom) (expr xto,yyto,zto) =
    begingroup ; save yfrom, yto ;
    yfrom := flow_y_pos(yyfrom) ;
    yto := flow_y_pos(yyto) ;
    if flow_points_initialized(xfrom,yfrom,xto,yto,6) :
        flow_xypoints[1] := flow_xy_right(xfrom,yfrom,zfrom,true) ;
        flow_xypoints[6] := flow_xy_right(xto,yto,zto,true) ;
        flow_xypoints[2] := flow_right_on_grid(1) ;
        flow_xypoints[5] := flow_right_on_grid(6) ;
        flow_xypoints[3] := flow_right_to_grid(2,5) ;
        flow_xypoints[4] := flow_right_to_grid(5,2) ;
        if not flow_valid_connection(xfrom,yfrom,xto,yto) :
          flow_xypoints[3] := flow_y_on_grid(2,yfrom,yto,zfrom) ;
          flow_xypoints[4] := flow_xy_on_grid(5,3) ;
        fi ;
        flow_draw_connection(n,xfrom,yyfrom,xto,yyto) ;
    fi ;
    endgroup
enddef ;

def flow_connect_top_top (expr n) (expr xfrom,yyfrom,zfrom) (expr xto,yyto,zto) =
    begingroup ; save yfrom, yto ;
    yfrom := flow_y_pos(yyfrom) ;
    yto := flow_y_pos(yyto) ;
    if flow_points_initialized(xfrom,yfrom,xto,yto,6) :
        flow_xypoints[1] := flow_xy_top(xfrom,yfrom,zfrom,true) ;
        flow_xypoints[6] := flow_xy_top(xto,yto,zto,true) ;
        flow_xypoints[2] := flow_up_on_grid(1) ;
        flow_xypoints[5] := flow_up_on_grid(6) ;
        flow_xypoints[3] := flow_up_to_grid(2,5) ;
        flow_xypoints[4] := flow_up_to_grid(5,2) ;
        if not flow_valid_connection(xfrom,yfrom,xto,yto) :
            flow_xypoints[3] := flow_x_on_grid(2,xfrom,xto,zfrom) ;
            flow_xypoints[4] := flow_xy_on_grid(3,5) ;
        fi ;
        %%%% begin experiment (todo: not value but just + and )
        if flow_dsp_y <> 0 :
            flow_xypoints[2] := flow_xypoints[2] shifted (0,flow_dsp_y) ;
            flow_xypoints[3] := flow_xypoints[3] shifted (0,flow_dsp_y) ;
            flow_xypoints[4] := flow_xypoints[4] shifted (0,flow_dsp_y) ;
            flow_xypoints[5] := flow_xypoints[5] shifted (0,flow_dsp_y) ;
        fi ;
        %%%% end experiment
        flow_draw_connection(n,xfrom,yyfrom,xto,yyto) ;
    fi ;
    endgroup
enddef ;

def flow_connect_bottom_bottom (expr n) (expr xfrom,yyfrom,zfrom) (expr xto,yyto,zto) =
    begingroup ; save yfrom, yto ;
    yfrom := flow_y_pos(yyfrom) ;
    yto := flow_y_pos(yyto) ;
    if flow_points_initialized(xfrom,yfrom,xto,yto,6) :
        flow_xypoints[1] := flow_xy_bottom(xfrom,yfrom,zfrom,true) ;
        flow_xypoints[6] := flow_xy_bottom(xto,yto,zto,true) ;
        flow_xypoints[2] := flow_down_on_grid(1) ;
        flow_xypoints[5] := flow_down_on_grid(6) ;
        flow_xypoints[3] := flow_down_to_grid(2,5) ;
        flow_xypoints[4] := flow_down_to_grid(5,2) ;
        if not flow_valid_connection(xfrom,yfrom,xto,yto) :
            flow_xypoints[3] := flow_x_on_grid(2,xfrom,xto,zfrom) ;
            flow_xypoints[4] := flow_xy_on_grid(3,5) ;
        fi ;
        %%%% begin experiment
        flow_xypoints[3] := flow_xypoints[3] shifted (flow_dsp_x,0) ;
        flow_xypoints[4] := flow_xypoints[4] shifted (flow_dsp_x,0) ;
        if flow_dsp_y<0 :
            flow_xypoints[2] := flow_xypoints[2] shifted (0,-flow_dsp_y) ;
            flow_xypoints[3] := flow_xypoints[3] shifted (0,-flow_dsp_y) ;
        elseif flow_dsp_y>0 :
            flow_xypoints[4] := flow_xypoints[4] shifted (0,flow_dsp_y) ;
            flow_xypoints[5] := flow_xypoints[5] shifted (0,flow_dsp_y) ;
        fi
        %%%% end experiment
        flow_draw_connection(n,xfrom,yyfrom,xto,yyto) ;
    fi ;
    endgroup
enddef ;

def flow_connect_bottom_top (expr n) (expr xfrom,yfrom,zfrom) (expr xto,yto,zto) =
    flow_reverse_connection := true ;
    flow_connect_top_bottom (n) (xto,yto,zto) (xfrom,yfrom,zfrom) ;
enddef ;

def flow_connect_right_left (expr n) (expr xfrom,yfrom,zfrom) (expr xto,yto,zto) =
    flow_reverse_connection := true ;
    flow_connect_left_right (n) (xto,yto,zto) (xfrom,yfrom,zfrom) ;
enddef ;

def flow_connect_top_left (expr n) (expr xfrom,yfrom,zfrom) (expr xto,yto,zto) =
    flow_reverse_connection := true ;
    flow_connect_left_top (n) (xto,yto,zto) (xfrom,yfrom,zfrom) ;
enddef ;

def flow_connect_bottom_left (expr n) (expr xfrom,yfrom,zfrom) (expr xto,yto,zto) =
    flow_reverse_connection := true ;
    flow_connect_left_bottom (n) (xto,yto,zto) (xfrom,yfrom,zfrom) ;
enddef ;

def flow_connect_top_right (expr n) (expr xfrom,yfrom,zfrom) (expr xto,yto,zto) =
    flow_reverse_connection := true ;
    flow_connect_right_top (n) (xto,yto,zto) (xfrom,yfrom,zfrom) ;
enddef ;

def flow_connect_bottom_right (expr n) (expr xfrom,yfrom,zfrom) (expr xto,yto,zto) =
    flow_reverse_connection := true ;
    flow_connect_right_bottom (n) (xto,yto,zto) (xfrom,yfrom,zfrom) ;
enddef ;

def flow_draw_test_shape(expr x, y) =
    flow_draw_shape(x,y,fullcircle,flow_shape_width/flow_grid_width,flow_shape_height/flow_grid_height) ;
enddef ;

def flow_draw_test_shapes =
    flow_draw_test_area ;
    for i=1 upto flow_max_x :
        for j=1 upto flow_max_y :
            flow_draw_test_shape(i,j) ;
            flow_chart_draw_label(i,j,"",textext("\ttx(" & decimal i & "," & decimal j & ")"))
        endfor ;
    endfor ;
enddef;

def flow_draw_test_area =
    pickup pencircle scaled .5flow_shape_line_width ;
    for i=1 upto flow_max_x + 1 :
        draw ((i,1) -- (i,flow_max_y+1)) flow_scaled_to_grid withcolor white/2 ;
    endfor ;
    for i=1 upto flow_max_y + 1 :
        draw ((1,i) -- (flow_max_x+1,i)) flow_scaled_to_grid withcolor white/2 ;
    endfor ;
enddef ;

def flow_show_connection(expr n, m) =

    flow_begin_chart(100+n,6,6) ;

        flow_draw_test_area ;

        flow_smooth   := true ;
        flow_arrowtip := true ;
        flow_dashline := true ;

        flow_draw_test_shape(2,2) ; flow_draw_test_shape(4,5) ;
        flow_draw_test_shape(3,3) ; flow_draw_test_shape(5,1) ;
        flow_draw_test_shape(2,5) ; flow_draw_test_shape(1,3) ;
        flow_draw_test_shape(6,2) ; flow_draw_test_shape(4,6) ;

        if     (m=1) :
            flow_connect_top_bottom    (0) (2,2,0) (4,5,0) ;
            flow_connect_top_bottom    (0) (3,3,0) (5,1,0) ;
            flow_connect_top_bottom    (0) (2,5,0) (1,3,0) ;
            flow_connect_top_bottom    (0) (6,2,0) (4,6,0) ;
        elseif (m=2) :
            flow_connect_top_top       (0) (2,2,0) (4,5,0) ;
            flow_connect_top_top       (0) (3,3,0) (5,1,0) ;
            flow_connect_top_top       (0) (2,5,0) (1,3,0) ;
            flow_connect_top_top       (0) (6,2,0) (4,6,0) ;
        elseif (m=3) :
            flow_connect_bottom_bottom (0) (2,2,0) (4,5,0) ;
            flow_connect_bottom_bottom (0) (3,3,0) (5,1,0) ;
            flow_connect_bottom_bottom (0) (2,5,0) (1,3,0) ;
            flow_connect_bottom_bottom (0) (6,2,0) (4,6,0) ;
        elseif (m=4) :
            flow_connect_left_right    (0) (2,2,0) (4,5,0) ;
            flow_connect_left_right    (0) (3,3,0) (5,1,0) ;
            flow_connect_left_right    (0) (2,5,0) (1,3,0) ;
            flow_connect_left_right    (0) (6,2,0) (4,6,0) ;
        elseif (m=5) :
            flow_connect_left_left     (0) (2,2,0) (4,5,0) ;
            flow_connect_left_left     (0) (3,3,0) (5,1,0) ;
            flow_connect_left_left     (0) (2,5,0) (1,3,0) ;
            flow_connect_left_left     (0) (6,2,0) (4,6,0) ;
        elseif (m=6) :
            flow_connect_right_right   (0) (2,2,0) (4,5,0) ;
            flow_connect_right_right   (0) (3,3,0) (5,1,0) ;
            flow_connect_right_right   (0) (2,5,0) (1,3,0) ;
            flow_connect_right_right   (0) (6,2,0) (4,6,0) ;
        elseif (m=7) :
            flow_connect_left_top      (0) (2,2,0) (4,5,0) ;
            flow_connect_left_top      (0) (3,3,0) (5,1,0) ;
            flow_connect_left_top      (0) (2,5,0) (1,3,0) ;
            flow_connect_left_top      (0) (6,2,0) (4,6,0) ;
        elseif (m=8) :
            flow_connect_left_bottom   (0) (2,2,0) (4,5,0) ;
            flow_connect_left_bottom   (0) (3,3,0) (5,1,0) ;
            flow_connect_left_bottom   (0) (2,5,0) (1,3,0) ;
            flow_connect_left_bottom   (0) (6,2,0) (4,6,0) ;
        elseif (m=9) :
            flow_connect_right_top     (0) (2,2,0) (4,5,0) ;
            flow_connect_right_top     (0) (3,3,0) (5,1,0) ;
            flow_connect_right_top     (0) (2,5,0) (1,3,0) ;
            flow_connect_right_top     (0) (6,2,0) (4,6,0) ;
        else :
            flow_connect_right_bottom  (0) (2,2,0) (4,5,0) ;
            flow_connect_right_bottom  (0) (3,3,0) (5,1,0) ;
            flow_connect_right_bottom  (0) (2,5,0) (1,3,0) ;
            flow_connect_right_bottom  (0) (6,2,0) (4,6,0) ;
        fi ;

    flow_end_chart ;

enddef ;

def flow_show_connections =
    for f=1 upto 10 :
        flow_show_connection(f,f) ;
    endfor ;
enddef ;

%D charts

def flow_clip_chart(expr minx, miny, maxx, maxy) =
    flow_cmin_x := minx ;
    flow_cmax_x := maxx ;
    flow_cmin_y := miny ;
    flow_cmax_y := maxy ;
enddef ;

def flow_begin_chart(expr n, maxx, maxy) =
    flow_new_chart ;
    flow_chart_figure := n ;
    flow_chart_scale := 1 ;
    if flow_chart_figure>0:
        beginfig(flow_chart_figure) ;
    fi ;
    flow_initialize_grid (maxx, maxy) ;
    bboxmargin := 0 ;
    flow_cmin_x := 1 ;
    flow_cmax_x := maxx ;
    flow_cmin_y := 1 ;
    flow_cmax_y := maxy ;
enddef ;

def flow_end_chart =
    begingroup ;
    save p, c ; path p, c ;
    flow_flush_shapes ;
    flow_flush_connections ;
    flow_flush_pictures ;
    flow_cmin_x := flow_cmin_x ;
    flow_cmax_x := flow_cmin_x+flow_cmax_x ;
    flow_cmin_y := flow_cmin_y-1 ;
    flow_cmax_y := flow_cmin_y+flow_cmax_y ;
    if flow_reverse_y :
        flow_cmin_y := flow_y_pos(flow_cmin_y) ;
        flow_cmax_y := flow_y_pos(flow_cmax_y) ;
    fi ;
    p := (((flow_cmin_x,flow_cmin_y)--(flow_cmax_x,flow_cmin_y)--
         (flow_cmax_x,flow_cmax_y)--(flow_cmin_x,flow_cmax_y)--cycle))
       flow_scaled_to_grid ;
    %draw p withcolor red ;
    if flow_chart_left_offset   <> 0 : p := p leftenlarged   flow_chart_left_offset   ; fi ;
    if flow_chart_right_offset  <> 0 : p := p rightenlarged  flow_chart_right_offset  ; fi ;
    if flow_chart_top_offset    <> 0 : p := p topenlarged    flow_chart_top_offset    ; fi ;
    if flow_chart_bottom_offset <> 0 : p := p bottomenlarged flow_chart_bottom_offset ; fi ;
    c := p enlarged flow_chart_clip_offset ;
    p := p enlarged flow_chart_offset ;
    clip currentpicture to c ;
    setbounds currentpicture to p ;
    endgroup ;
    currentpicture := currentpicture scaled flow_chart_scale ;
    if flow_chart_figure>0:
        endfig ;
    fi ;
enddef ;

def flow_new_shape(expr x, y, n) =
    if known n :
        if (x>0) and (x<=flow_max_x) and (y>0) and (y<=flow_max_y) :
            flow_draw_shape(x,y,some_shape_path(n), flow_shape_width/flow_grid_width, flow_shape_height/flow_grid_height) ;
        else :
            message ("shape outside grid ignored") ;
        fi ;
    else :
        message ("shape not known" ) ;
    fi ;
enddef ;

def flow_begin_sub_chart =
    begingroup ;
    save    flow_shape_line_width, flow_connection_line_width ;
    save    flow_shape_line_color, flow_shape_fill_color, flow_connection_line_color ;
    string  flow_shape_line_color, flow_shape_fill_color, flow_connection_line_color ;
    save    flow_smooth, flow_arrowtip, flow_dashline, flow_peepshape ;
    boolean flow_smooth, flow_arrowtip, flow_dashline, flow_peepshape ;
enddef ;

def flow_end_sub_chart =
    endgroup ;
enddef ;

