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