%% bondgraph.mp
%% Copyright 2007 Henrik Tidefelt
% 
% This work may be distributed and/or modified under the 
% conditions of the LaTeX Project Public License, either version 1.3 
% of this license or (at your option) any later version.
% The latest version of this license is in 
% http://www.latex-project.org/lppl.txt 
% and version 1.3 or later is part of all distributions of LaTeX 
% version 2005/12/01 or later.
% 
% This work has the LPPL maintenance status `maintained�.
% 
% The Current Maintainer of this work is Henrik Tidefelt,
% tidefelt@isy.liu.se.
% 
% This work, referred to as blockdraw.mp, consists of the files
%   shiftoff.mp
%   blockdraw.mp
%   bondgraph.mp
%   blockdraw_mp.pdf
%
%
% === Notes regarding the backward incompatible changes of 2007-01-21. ===
%
% As it was discovered that MetaPost uses the verbatimtex construct only in
% the file it appears in, it was concluded that package files cannot contain
% any content that depends on the LaTeX preamble.  In other words, any
% btex ... etex constructs must be moved from package files to the application
% sources from where the package files are included.
%
% With the definition of, say, p-junction blocks in the application source,
% it is easy to change notation from the letters "p" and "s" to the numbers
% "0" and "1".
%
% The obvious drawback, beside backward incompatibility, is that this will
% cause a lot of code replication, making bond graph source files
% less concise, and require multiple file search-and-replace actions when
% changing junction notation for a collection of graphs.  However, since
% MetaPost only allows literal character strings in the btex ... etex
% construct, these inconveniences seem impossible to work around.  Ideas,
% anyone?

input blockdraw;
implicitdraw := false;

boolean junctionimplicitdraw;
junctionimplicitdraw := true;

boolean useopenbonds;
useopenbonds := false;

connectionlw := 0.5pt;
ahlength := 3mm;
bboxmargin := 1.5mm;

def withbbmargin(expr pic ) =
  begingroup
    save tmppic;
    picture tmppic;
    tmppic = pic;
    setbounds tmppic to bbox pic;
    tmppic
  endgroup
enddef;

def junction(expr txt, z ) =
  begingroup
    save tmppic;
    picture tmppic;
    tmppic := nullpicture;
    addto tmppic also shiftoff( txt scaled textscale, to_center );
    setbounds tmppic to (-smallblockr,-smallblockr)--(-smallblockr,smallblockr)--(smallblockr,smallblockr)--(smallblockr,-smallblockr)--cycle;
    tmppic := tmppic shifted z;
    if junctionimplicitdraw:
      draw tmppic;
    fi
    tmppic
  endgroup
enddef;


def junctionlbl(expr txt, arrowdir, lbl, z, arrow ) =
  begingroup
    save tmppic, ahlength, p, d;
    ahlength := 1mm;
    path p;
    numeric d;
    picture tmppic;
    tmppic := nullpicture;
    addto tmppic also shiftoff( txt scaled textscale, to_center );

    if arrowdir = to_top:
      p := ((lrcorner tmppic)--(urcorner tmppic)) shifted ( z + (connectionlw + 2*ahlength*sind(0.5*ahangle),0) );
      d = to_rt;
    elseif arrowdir = to_lft:
      p := ((urcorner tmppic)--(ulcorner tmppic)) shifted ( z + (0,connectionlw + 2*ahlength*sind(0.5*ahangle)) );
      d = to_top;
    elseif arrowdir = to_bot:
      p := ((urcorner tmppic)--(lrcorner tmppic)) shifted ( z + (connectionlw + 2*ahlength*sind(0.5*ahangle),0) );
      d = to_rt;
    elseif arrowdir = to_rt:
      p := ((ulcorner tmppic)--(urcorner tmppic)) shifted ( z + (0,connectionlw + 2*ahlength*sind(0.5*ahangle)) );
      d = to_top;
    fi

    setbounds tmppic to (-smallblockr,-smallblockr)--(-smallblockr,smallblockr)--(smallblockr,smallblockr)--(smallblockr,-smallblockr)--cycle;
    tmppic := tmppic shifted z;

    if junctionimplicitdraw:
      if arrow:
	drawarrow p withpen pencircle scaled connectionlw;
      fi
      draw conlabel( d, lbl, mspoint( p, 0.5, 0 ) );
      draw tmppic;
    fi
    tmppic
  endgroup
enddef;

def terminal(expr d, txt, z ) =
  begingroup
    save tmppic;
    picture tmppic;
    tmppic := shiftoff( withbbmargin( txt scaled textscale ), d ) shifted ( z - smallblockr * dir_to( d ) );
    if junctionimplicitdraw:
      draw tmppic;
    fi
    tmppic
  endgroup  
enddef;

def ignorepicture(expr pic ) =
  show 0
enddef;

def terminalto(expr j, txt, z ) =
  begingroup
    ignorepicture( terminal( to_dir( z - (center j) ), txt, z ) );
    bgconnect( pointpicture( z ) , j )
  endgroup
enddef;

def terminalfr(expr j, txt, z ) =
  begingroup
    ignorepicture( terminal( to_dir( z - (center j) ), txt, z ) );
    bgconnect( j, pointpicture( z ) )
  endgroup
enddef;

def bgconnect(expr pica, picb ) =
  begingroup
    save d;
    pair d;
    if (center pica) = (center picb):
      d := (1,0);
    else:
      d := unitvector( (center picb) - (center pica) );
    fi
    ((center pica)+d*smallblockr)--((center picb)-d*smallblockr)
  endgroup
enddef;

def bond(expr p ) =
  begingroup
    save t;
    numeric t;
    t := arctime arclength(p) - ahlength of p;
    save z;
    pair z;
    z = (point t of p) + dir(angle(direction t of p)+90) * ahlength*sind(0.5*ahangle);
    save res;
    path res;
    if useopenbonds:
      draw p--z withpen pencircle scaled connectionlw
    else:
      draw p withpen pencircle scaled connectionlw;
      res = (subpath (t,infinity) of p)--z--cycle;
      filldraw res withpen pencircle scaled connectionlw
    fi
  endgroup
enddef;

def causalmark(expr p, where ) =
  begingroup
    save d;
    numeric d;
    d = angle( direction where of p );
    if where = 0:
      d := d + 180;
    fi
    save z;
    pair z;
    z = ( point where of p ) + dir(d) * connectionlw;
    save res;
    path res;
    res = (z+dir(d+90)*ahlength*sind(0.5*ahangle))--(z+dir(d-90)*ahlength*sind(0.5*ahangle));
    draw res withpen pencircle scaled (2*connectionlw)
  endgroup
enddef;

def hbond(expr p ) =
  begingroup
    bond( p );
    causalmark( p, infinity )
  endgroup
enddef;

def tbond(expr p ) =
  begingroup
    bond( p );
    causalmark( p, 0 )
  endgroup
enddef;

def flowlabel(expr p, txt ) =
%  the midpoint has typically singular direction, so we can't do the obvious:
%  draw( conlabel( to_dir( dir( angle( direction (0.5*length(p)) of p ) - 90 ) ), txt, mspoint( p, 0.5, 0 ) ) )
  draw conlabel( to_dir( dir( angle( (point infinity of p) - (point 0 of p) ) - 90 ) ), txt, mspoint( p, 0.5, 0 ) ) withpen pencircle scaled connectionlw
enddef;

def effortlabel(expr p, txt ) =
%  the midpoint has typically singular direction, so we can't do the obvious:
%  draw( conlabel( to_dir( dir( angle( direction (0.5*length(p)) of p ) + 90 ) ), txt, mspoint( p, 0.5, 0 ) ) )
  draw conlabel( to_dir( dir( angle( (point infinity of p) - (point 0 of p) ) + 90 ) ), txt, mspoint( p, 0.5, 0 ) ) withpen pencircle scaled connectionlw
enddef;