.PS
# Traintracks.m4
# https://tex.stackexchange.com/questions/729226/implementing-train-tracks-in-latex
gen_init
define(`robustcode_',1)

divert(-1)
########################################################################
                       `Default sizes'
  define(`sectionlen',linewid)
  define(`railgauge',(linewid/5))
  define(`railthick',`1.5')
  define(`buttlen',`(railthick bp__ * 3)')
  define(`sleeperthick',`0.4')
  define(`buttthick',`(sleeperthick*2)')
   
                       `straight(position,direction,keys)
                        keys: length=expr; (default sectionlen)
                              gauge=expr; (default railgauge)
                              tiecount=expr; (number of sleepers)
                              tie=attributes;
                              rail=attributes;'
define(`straight',`ifelse(`$1',,,move to `$1';) ifelse(`$2',,,`setdir_(`$2')')
  pushkeys_(`$3',
   `length:sectionlen:N; gauge:railgauge; rail::N; tiecount:3:; tie::N;')dnl
  for straighti=1 to m4tiecount do {
   {line from rvec_((straighti-1/2)*m4length/m4tiecount, m4gauge/2+buttlen/2) \
         to   rvec_((straighti-1/2)*m4length/m4tiecount,-m4gauge/2-buttlen/2) \
         thick sleeperthick m4tie }}
  {strail(m4gauge/2)}; {strail(-m4gauge/2)}; move to rvec_(m4length,0)dnl
  popdef(`m4tie',`m4tiecount',`m4length',`m4gauge',`m4rail') ')
define(`strail',
 `{line from rvec_(0,`$1') to rvec_(sectionlen,`$1') thick railthick m4rail
   line from rvec_(0,-buttlen/2) to rvec_(0,buttlen/2) thick buttthick}
  {line from rvec_(0,`$1'-buttlen/2) to rvec_(0,`$1'+buttlen/2) \
     thick buttthick m4rail} ')

                       `curve(position,direction,L|R,keys)
                        keys: radius=expr; (default sectionlen*2)
                              gauge=expr; (default railgauge)
                              tiecount=expr; (number of sleepers)
                              tie=attributes;
                              rail=attributes;
                        Calls to robust rvec_r instead of rvec_ are for
                        use in pic loops'
define(`curve',`ifelse(`$1',,,move to `$1';) ifelse(`$2',,,`setdir_(`$2')')
  pushkeys_(`$4',
   `radius:sectionlen*2; gauge:railgauge; rail::N; tiecount:3:; tie::N;')dnl
  pushdef(`m4pm',`ifelse(`$3',,+,`$3',L,+,-)')dnl
  M4C: rvec_r(0,m4pm m4radius) # robust rvec
  m4rs = rp_ang*rtod_-(m4pm 90); m4rf = m4rs m4pm 30
  for curvei=1 to m4tiecount do {{ line \
    from M4C+(Rect_(m4radius+m4gauge/2+buttlen/2,\
      m4rs+(curvei-1/2)*(m4rf-m4rs)/m4tiecount))\
    to   M4C+(Rect_(m4radius-m4gauge/2-buttlen/2,\
      m4rs+(curvei-1/2)*(m4rf-m4rs)/m4tiecount)) thick sleeperthick m4tie }}
  move to M4C+(Rect_(m4radius,m4rs))
  {crail(M4C,m4radius-(m4pm m4gauge/2), m4gauge/2,m4pm)}
  {crail(M4C,m4radius+(m4pm m4gauge/2),-m4gauge/2,m4pm)}
  move to M4C+(Rect_(m4radius,m4rf)); setdir_(rp_ang*rtod_ m4pm 30)
  popdef(`m4tie',`m4tiecount',`m4rail',`m4gauge',`m4radius',`m4pm') ')

define(`crail',       #`crail(center,rad,offset,+|-)'
 `move to rvec_r(0,`$3'); crbuttf = buttlen/2/(`$2')
  {arc ifelse(`$4',-,cw) to rvec_r((`$2')/2,`$4'((1-sqrt(3)/2)*(`$2'))) \
     with .c at `$1' thick railthick m4rail
   line from (-crbuttf between Here and `$1') \
        to   ( crbuttf between Here and `$1') thick buttthick}
  {line from (-crbuttf between Here and `$1') \
        to   ( crbuttf between Here and `$1') thick buttthick} ')

########################################################################
divert(0)dnl

[ straight
  {curve; curve(,,R); straight; curve(,,R); curve; straight}
  for i=1 to 5 do { straight } ]

[ straight
  for i=1 to 6 do { curve(,,R) }
  straight
 {T: Here
  curve(,,R,rail=outlined "red"); curve(,,,rail=outlined "red")
  straight
  crad = sectionlen*2-(Here.y-T.y)
  for i=1 to 6 do { curve(,,R,radius=crad) }
  straight
  define rgbpurp { rgbstring(0.5,0,1) }
  { curve(,,, rail=outlined rgbpurp); curve(,,R, rail=outlined rgbpurp)
    for i=1 to 5 do { straight(,180) } }
  for i=1 to 3 do { straight(,0) }
  for i=1 to 6 do { curve(,,R,radius=crad) }
  for i=1 to 3 do { straight } }
  for i=1 to 5 do { straight } ] with .nw at last [].sw+(0,-0.2)

[ for dir=90 to 570 by 240 do {
    curve(,dir,,tiecount=5;tie=outlined "gray" thick 2.5)
    curve(,   ,,tiecount=5;tie=outlined "gray" thick 2.5) }
  ] with .nw at 1st [].ne+(0.2,0)

.PE
