% swimgraf.sty % To produce graphs of swim times using PSTricks tools % Martin J. Osborne 2002/5 Version 1.0 % http://www.economics.utoronto.ca/osborne % For documentation and files of records, see % http://www.economics.utoronto.ca/osborne/latex % This material is subject to the LaTeX Project Public License. See % http://www.ctan.org/tex-archive/help/Catalogue/licenses.lppl.html % for details. \RequirePackage{mathpazo} \RequirePackage{pstricks} \RequirePackage{pstcol,pst-plot} \RequirePackage{keyval} \newif\ifswg@eventmismatch \newif\ifswg@continue \newif\ifswg@plotlabel\swg@plotlabeltrue \newif\ifswg@a\swg@afalse \newif\ifswg@b\swg@bfalse \newif\ifswg@c\swg@cfalse \newif\ifswg@d\swg@dfalse \newif\ifswg@e\swg@efalse \newif\ifswg@f\swg@ffalse \newif\ifswg@j\swg@jfalse \newif\ifswg@n\swg@nfalse \newif\ifswg@w\swg@wfalse \newif\ifswg@x\swg@xfalse \newif\ifswg@y\swg@yfalse \newif\ifswg@z\swg@zfalse \newif\ifswg@agerec\swg@agerecfalse \newif\ifswg@screcinrange\swg@screcinrangefalse \newif\ifswg@lcrecinrange\swg@lcrecinrangefalse \newif\ifswg@continue\swg@continuetrue \newif\ifswg@star \newif\ifswg@hyphen \newif\ifswg@qm \newif\ifswg@colon \newif\ifswg@period \newif\ifswg@plus \newif\ifswg@matchfound \newif\ifswg@nmn \newif\ifswg@nmd \newif\ifswg@eventfound \newif\ifswg@inrange \newif\ifswg@indescription\swg@indescriptionfalse \newif\ifswg@putyr\swg@putyrfalse \newdimen\swg@monthbaseh \newdimen\swg@titlebaseh \newdimen\swg@tempdima \newdimen\swg@tempdimb \newdimen\swg@tempdimh \newdimen\swg@tempdiml \newdimen\swg@avgw \newdimen\swg@avgh \newdimen\swg@top \newdimen\swg@bottom \newdimen\swg@labelh \newdimen\swg@lpos \newdimen\swg@keyh \newcount\swg@htime \newcount\swg@ltime \newcount\swg@lhvytime \newcount\swg@age \newcount\swg@endage \newcount\swg@hvylines \newcount\swg@lghlines \newcount\swg@midy \newcount\swg@day \newcount\swg@month \newcount\swg@year \newcount\swg@daycount \newcount\swg@diycount \newcount\swg@startday \newcount\swg@startyear \newcount\swg@startdiy \newcount\swg@endday \newcount\swg@enddaycount \newcount\swg@enddiy \newcount\swg@bdaydiy \newcount\swg@mid \newcount\swg@i \newcount\swg@recsday \newcount\swg@receday \newcount\swg@agecount \newcount\swg@vlabelsecs \newcount\swg@vlabelmins \newcount\@tempcntc \newcount\@tempcntd \newcount\@tempcntx \newcount\@tempcnty \newcount\@tempcntz \newcount\@tempcntyr % If you use the landscape option of the article style and % 'lettersize' paper, the landgraph option for swimgraf defines % margins etc. that seem to allow you to use the whole page % (I don't understand the logic of the topmargin) \DeclareOption{landgraph}{\textwidth 11in\textheight 8.5in \oddsidemargin -1in\evensidemargin -1in\topmargin -1.5in} % Age-specific standards/records \DeclareOption{a}{\swg@atrue} \DeclareOption{b}{\swg@btrue} \DeclareOption{c}{\swg@ctrue} \DeclareOption{d}{\swg@dtrue} \DeclareOption{e}{\swg@etrue} \DeclareOption{f}{\swg@ftrue} % Age-open standards/records \DeclareOption{j}{\swg@jtrue} \DeclareOption{n}{\swg@ntrue} \DeclareOption{w}{\swg@wtrue} \DeclareOption{x}{\swg@xtrue} \DeclareOption{y}{\swg@ytrue} \DeclareOption{z}{\swg@ztrue} \input swimgraf.cfg \ProcessOptions %---------------------------------------% % LABELS ON VERTICAL AXIS % %---------------------------------------% % Modify PSTricks labels on vertical axis % to convert seconds into minutes and seconds \def\psvlabel#1{% \swg@tominssecso#1\relax \swg@timeresult} %-------------------------------------% % ERRORS % %-------------------------------------% % Following modeled on PSTricks error messages \def\swg@err#1#2{% \begingroup \newlinechar`\^^J \edef\swg@tempc{#2}% \expandafter\errhelp\expandafter{\swg@tempc}% \typeout{^^JSwimgraph error: type \space H \space for immediate help.}% \errmessage{#1}% \endgroup} \def\swg@ehpa{% This result will be ignored.^^J Type \space \space to proceed or \space X \space to quit.} \def\swg@ehpb{% Your command was ignored. Will recover best I can.^^J Type \space \space to proceed or \space X \space to quit.} \def\swg@ehpc{% You need to fix this error before proceding.^^J Type \space X \space to quit.} %-------------------------------------% % DATE UTILITY % %-------------------------------------% % \swg@date year-month-day defines \swg@year, \swg@month, \swg@day, % \swg@diycount (day in year), and \swg@daycount (index of day since 2000/1/1) % Doesn't account for leap years \def\swg@date#1-#2-#3 {% \swg@year=#1 \swg@month=#2 \swg@day=#3 \ifnum\swg@month=1 \else\ifnum\swg@month=2 \advance\swg@day by31 \else\ifnum\swg@month=3 \advance\swg@day by59 \else\ifnum\swg@month=4 \advance\swg@day by90 \else\ifnum\swg@month=5 \advance\swg@day by120 \else\ifnum\swg@month=6 \advance\swg@day by151 \else\ifnum\swg@month=7 \advance\swg@day by181 \else\ifnum\swg@month=8 \advance\swg@day by212 \else\ifnum\swg@month=9 \advance\swg@day by243 \else\ifnum\swg@month=10 \advance\swg@day by273 \else\ifnum\swg@month=11 \advance\swg@day by304 \else\ifnum\swg@month=12 \advance\swg@day by334 \else \swg@err{The month \the\swg@month\space (in the date #1-#2-#3) does not exist}\swg@ehpc \fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi \swg@diycount=\swg@day % \swg@daycount is index of day since 2000/1/1 \swg@daycount=\swg@year \advance\swg@daycount by-2000 % doesn't account for leap years \multiply\swg@daycount by365 \advance\swg@daycount by\swg@diycount } %-------------------------------------% % TIME UTILITIES % %-------------------------------------% % Test time format % The function \in@ is in LaTeX % \in@{x}{y} sets \ifin@ true if the string y contains the string x % and false otherwise % Here it is in case it disappears: %\def\in@#1#2{% % \def\in@@##1#1##2##3\in@@{% % \ifx\in@##2\in@false\else\in@true\fi}% % \in@@#2#1\in@\in@@} %\newif\ifin@ % Given time in various formats, define \swg@mins, \swg@secs, and \swg@msecs. % Formats accepted: m:s.x (put m in \swg@mins, s in \swg@secs, x in \swg@msecs) % m:s (put m in \swg@mins, s in \swg@secs, and 0 in \swg@msecs) % s.x (put s in \swg@secs and x in \swg@msecs) % s (put s in \swg@secs and 0 in \swg@msecs) \def\swg@parsetime#1{% \in@{:}{#1}% \ifin@% \in@{.}{#1}% \ifin@% \swg@t@msm#1\relax% \else% \swg@t@ms#1\relax% \fi% \else% \in@{.}{#1}% \ifin@% \swg@t@sm#1\relax% \else% \swg@t@s#1\relax% \fi% \fi% } \def\swg@t@msm#1:#2.#3\relax{% \def\swg@mins{#1}\def\swg@secs{#2}\def\swg@msecs{#3}} \def\swg@t@ms#1:#2\relax{% \def\swg@mins{#1}\def\swg@secs{#2}\def\swg@msecs{0}} \def\swg@t@sm#1.#2\relax{% \def\swg@mins{0}\def\swg@secs{#1}\def\swg@msecs{#2}} \def\swg@t@s#1\relax{% \def\swg@mins{0}\def\swg@secs{#1}\def\swg@msecs{0}} % Convert time to seconds.milliseconds (OK for argument to be in this format) % Puts result in \swg@timeresult % Use syntax \expandafter\swg@tosecs\yourtime\relax % Result is in \swg@timeresult \def\swg@tosecs#1\relax{% \swg@parsetime{#1}% \@tempcnta=\swg@mins% \multiply\@tempcnta by60% \advance\@tempcnta by\swg@secs\relax% \edef\swg@timeresult{\the\@tempcnta.\swg@msecs}% } % Same, but ignore any milliseconds \def\swg@tosecso#1\relax{% \swg@parsetime{#1}% \@tempcnta=\swg@mins% \multiply\@tempcnta by60% \advance\@tempcnta by\swg@secs\relax% \edef\swg@timeresult{\the\@tempcnta}% } % Convert time to minutes:seconds.msecs if minutes > 0, else seconds.msecs % (OK for argument to already be in this format) % Puts result in \swg@timeresult % Use syntax \expandafter\swg@tominssecs\yourtime\relax \def\swg@tominssecs#1\relax{% \swg@parsetime{#1}% \@tempcnta=\swg@secs% \divide\@tempcnta by60% \@tempcntc=\@tempcnta% \advance\@tempcntc by\swg@mins% \multiply\@tempcnta by60% \@tempcntb=\swg@secs% \advance\@tempcntb by-\@tempcnta% \ifnum\@tempcntc=0% \edef\swg@timeresult{\the\@tempcntb.\swg@msecs}% \else\ifnum\@tempcntb<10% \edef\swg@timeresult{\the\@tempcntc:0\the\@tempcntb.\swg@msecs}% \else% \edef\swg@timeresult{\the\@tempcntc:\the\@tempcntb.\swg@msecs}% \fi\fi% } % Convert time to minutes:seconds (with minutes = 0 for times less than % a minute) % (OK for argument to already be in this format) % Puts result in \swg@timeresult % Use syntax \expandafter\swg@tominssecso\yourtime\relax \def\swg@tominssecso#1\relax{% \swg@parsetime{#1}% \@tempcnta=\swg@secs% \divide\@tempcnta by60% \@tempcntc=\@tempcnta% \advance\@tempcntc by\swg@mins% \multiply\@tempcnta by60% \@tempcntb=\swg@secs% \advance\@tempcntb by-\@tempcnta% \ifnum\@tempcntb<10% \edef\swg@timeresult{\the\@tempcntc:0\the\@tempcntb}% \else% \edef\swg@timeresult{\the\@tempcntc:\the\@tempcntb}% \fi% } %-------------------------------------% % RECORDS % %-------------------------------------% % % \swg@readrec#1#2: read records from file % #1: record type (w, n, ...) % #2: event name [e.g. "50 free"] % Defines % \swg@reclabel (label for record as it appears on graph) % \swg@screc (short course record) % \swg@lcrec (long course record) \def\swg@readrec#1#2 { % need \edef here: if #2 is a macro, which it is in this application, it % needs to be expanded when \swg@event is defined \edef\swg@event{#2} % to get rid of CR, which is converted to space, at end of line that is read \endlinechar=-1 \openin1=\swg@recfile % first line of file is comment % second line is label (as it appears on graph) \read1 to\@templine \read1 to\swg@reclabel \loop \read1 to\@templine \ifx\@templine\swg@event\swg@eventmismatchfalse \else\swg@eventmismatchtrue\fi \ifeof1\relax\swg@eventmismatchfalse\else\fi \ifswg@eventmismatch \repeat \ifeof1 \def\swg@screc{0} \def\swg@lcrec{0} \else \read1 to \swg@sc \read1 to \swg@lc \typeout{\swg@reclabel: \swg@sc\space(SC), \swg@lc\space(LC)} \expandafter\swg@tosecs\swg@sc\relax \edef\swg@screc{\swg@timeresult} \expandafter\swg@tosecs\swg@lc\relax \edef\swg@lcrec{\swg@timeresult} \fi \endlinechar=13 \closein1 } % Draw line, given record type and times, and start and end days. % #1: type of record: w, n, ... (needed to determine color) % #2: SC record time % #3: LC record time % #4: start day % #5: end day \def\swg@xrecline#1#2#3#4#5{% \def\swg@recsccolor{\csname swg@#1sccolor\endcsname} \def\swg@reclccolor{\csname swg@#1lccolor\endcsname} % if no record on file, or if above htime, don't draw any line \edef\@argii{#2}\edef\@argiii{#3}\def\@zero{0} \swg@screcinrangefalse \swg@lcrecinrangefalse \swg@tempdima=\@argii mm \swg@tempdimb=\@argiii mm \swg@tempdimh=\the\swg@htime mm \swg@tempdiml=\the\swg@ltime mm \ifx\@argii\@zero \else \ifdim\swg@tempdima<\swg@tempdimh \ifdim\swg@tempdima>\swg@tempdiml \swg@screcinrangetrue \psline[linecolor=\swg@recsccolor,linewidth=\swg@reclinewidth]% (#4,#2)(#5,#2) \fi \else \ifswg@screcinrange\else\typeout{Note: SC record is out of range.} \fi \fi \ifswg@screcinrange\else\typeout{Note: SC record is out of range.}\fi \fi \ifx\@argiii\@zero \else \ifdim\swg@tempdimb<\swg@tempdimh \ifdim\swg@tempdimb>\swg@tempdiml \swg@lcrecinrangetrue \psline[linecolor=\swg@reclccolor,linewidth=\swg@reclinewidth]% (#4,#3)(#5,#3) \fi \else \ifswg@lcrecinrange\else\typeout{Note: LC record is out of range.} \fi \fi \ifswg@lcrecinrange\else\typeout{Note: LC record is out of range.} \fi \fi \let\psunit=\psxunit \pssetlength{\swg@avgw}{#4} \psaddtolength{\swg@avgw}{#5} \divide\swg@avgw by2 \let\psunit=\psyunit % If there is no short course record \ifx\@argii\@zero % then if there is no long course record, do nothing \ifx\@argiii\@zero % and if there is a long course record and it is in the range, label it \else \ifswg@lcrecinrange \def\swg@avghh{#3} \rput*[framesep=0pt](\swg@avgw,\swg@avghh)% {\color{\swg@recsccolor}\swg@reclabel} \fi \fi % If there is a short course record \else % but no long course record, just label the short course record \ifx\@argiii\@zero \ifswg@screcinrange \def\swg@avghh{#2} \rput*(\swg@avgw,\swg@avghh){\color{\swg@recsccolor}\swg@reclabel} \fi % and if there are both long course and short course records, label them \else \ifswg@lcrecinrange \ifswg@screcinrange \pssetlength{\swg@avgh}{#2} \psaddtolength{\swg@avgh}{#3} \divide\swg@avgh by2 \rput*(\swg@avgw,\swg@avgh){\color{\swg@recsccolor}\swg@reclabel} \else \def\swg@avghh{#3} \rput*(\swg@avgw,\swg@avghh){\color{\swg@recsccolor}\swg@reclabel} \fi \else \ifswg@screcinrange \def\swg@avghh{#2} \rput*(\swg@avgw,\swg@avghh){\color{\swg@recsccolor}\swg@reclabel} \fi \fi \fi \fi } % Read record from file and pass appropriate parameters to \swg@xrecline % #1: record type (w, n, ...) % #2: event name (e.g. "50 free") % #3: start day % #4: end day \def\swg@recline#1#2#3#4{ % name of record file: like fw.dat (female), mw.dat (male) for age-open % and fw11.dat (female), mw11.dat (male) for age-specific (age 11) \def\swg@recfile{% \swg@filepath\swg@sex #1\ifswg@agerec\the\swg@agecount\fi.dat} \IfFileExists{\swg@recfile}{ \swg@readrec{#1}{#2} \swg@xrecline{#1}{\swg@screc}{\swg@lcrec}{#3}{#4} }{\typeout{FYI: file \swg@recfile\space does not exist.}} } % % Line for age-specific record, series #1 (a, b, ...) % Calls \swg@recline \def\swg@agerecline#1{% \swg@recsday=0 \swg@receday=\swg@bdaydiy \advance\swg@receday by-\swg@startdiy \ifnum\swg@startdiy>\swg@bdaydiy\advance\swg@receday by365\fi \swg@agecount=\swg@age \loop \ifnum\swg@agecount=\swg@endage\swg@receday=\swg@endday\fi % \typeout{Swimmer's age: \the\swg@agecount} % \typeout{sday: \the\swg@recsday. eday: \the\swg@receday} % braces enclosing next two lines are essential, because \swg@recline % includes a \loop {\swg@recline{#1}{\swg@dist\swg@strokex}% {\the\swg@recsday}{\the\swg@receday}} \ifnum\swg@agecount<\swg@endage\advance\swg@agecount by1 \swg@recsday=\swg@receday \advance\swg@receday by365 \repeat } %-------------------------------------% % PUTMONTH % %-------------------------------------% % Put month labels on graph % Draws a vertical line at the beginning of the month % and puts the month name. % \@tempcntb counts day in year. Is set to #3 by \swg@putmonth. % \@tempcntd counts day since base date. % \@tempcntc is x-coordinate of vertical line for month % It is initialized at 0; upon entering the macro, its value is the % x-coordinate for the line for the (start of the) previous month. The % macro increases its value to the x-coordinate for the start of the % current month, by % increasing it by #3 - \@tempcntb to a maximum of \swg@endday % #1: month name % #2: # of days in month % #3: # of days in year till 1st of month \def\swg@putmonth#1#2#3{ \@tempcnta=#3% \advance\@tempcntd by-\@tempcntc \advance\@tempcntc by#3\advance\@tempcntc by-\@tempcntb \advance\@tempcntd by\@tempcntc \ifnum\@tempcntd<\swg@enddaycount % don't put vertical line if first day is first of month \ifnum\@tempcntc>0 \ifnum\@tempcnta=0% at start of new year \def\swg@linecolor{\swg@yearlinecolor}% \else \def\swg@linecolor{\swg@monthlinecolor}% \fi \psline[linecolor=\swg@linecolor]% (\the\@tempcntc.5,\swg@ltime)(\the\@tempcntc.5,\swg@htime)% \fi \else\@tempcntc=\swg@endday % \@tempcntc gets stuck at \swg@endday \fi \advance\@tempcntc by#2 \ifnum\@tempcntc<\swg@endday \@tempcnta=#2\divide\@tempcnta by2 \advance\@tempcntc by-\@tempcnta \rput[B](\@tempcntc,\swg@monthbaseh){#1} \advance\@tempcntc by\@tempcnta \fi \advance\@tempcntc by-#2 \@tempcntb=#3} %-------------------------------------% % UTILITIES % %-------------------------------------% % % \end is a terminator % Macro checks if second character of line is =. Line must contain at least % 3 characters, else macro will bomb \def\swg@parsedataline#1#2#3\end{ \ifx#2= \def\swg@switch{#1} \def\swg@content{#3} \else\swg@err{Bad syntax in file '\swg@datafile' on line '\swg@fileline':^^J\space\space second character is not '='}\swg@ehpc \fi } % Parser for line of swimmer's data file giving times \def\swg@parseresultline#1 #2 #3\end{ \def\swg@fdist{#1} \def\swg@fstroke{#2} \def\swg@ftime{#3}} \def\swg@parsedatline#1 #2\end{ \def\swg@datargi{#1} \def\swg@datargii{#2}} % Read data from \swg@datafile and transfer appropriate parts of it to % swgs1.tmp (short course times), swgs1d.tmp (corresponding meet names), % swgl1.tmp (long course times), swgl1d.tmp (corresponding meet names), % and also similar files with 2 suffixes [for second series; not yet % implemented]. The time files are in the format to be read by PSTricks' % \readdata, and the meet name files are used by \swg@plotlabel. % #1: event distance. #2: stroke. \def\swg@readdata#1#2 { \swg@matchfoundfalse \edef\swg@argi{#1} \edef\swg@argii{#2} % to get rid of CR, which is converted to space, at end of line that is read \endlinechar=-1 \openin0=\swg@datafile \immediate\openout1=swgs1.tmp % short course times, series 1 \immediate\openout2=swgs1d.tmp % short course descriptions, series 1 \immediate\openout3=swgl1.tmp % long course times, series 1 \immediate\openout4=swgl1d.tmp % short course descriptions, series 1 % first three lines are ignored (name and birthdate have already been read) \read0 to\swg@fileline \read0 to\swg@fileline \read0 to\swg@fileline \read0 to\swg@fileline \loop \read0 to\swg@fileline % Next line necessary because blank line is read at end of file, before % \ifeof0 is true. It also allows all lines starting % to be ignored. \ifx\swg@fileline\@empty \else \expandafter\swg@parsedataline\swg@fileline\end \if\swg@switch m \ifswg@eventfound\swg@txdata\fi \let\swg@meetname=\swg@content %\typeout{Meet: \swg@meetname} \else\if\swg@switch d \ifswg@eventfound\swg@txdata\fi \let\swg@meetdate=\swg@content %\typeout{Date: \swg@meetdate} \else\if\swg@switch c \global\let\swg@fcourse=\swg@content \else\if\swg@switch n \global\let\swg@s@graphnote=\swg@content \else\if\swg@switch r \ifswg@eventfound\swg@txdata\fi \global\let\swg@descmod=\swg@switch \swg@checkdata \else\if\swg@switch u \ifswg@eventfound\swg@txdata\fi \global\let\swg@descmod=\swg@switch \swg@checkdata \else\if\swg@switch R \ifswg@eventfound\swg@txdata\fi \global\let\swg@descmod=\swg@switch \swg@checkdata \fi\fi\fi\fi\fi\fi\fi \fi \ifeof0\relax\swg@continuefalse\else\swg@continuetrue\fi \ifswg@continue \repeat \endlinechar=13 \ifswg@eventfound\swg@txdata\fi \closein0 \immediate\closeout1 \immediate\closeout2 \immediate\closeout3 \immediate\closeout4 \ifswg@matchfound\else \swg@err{No data for event '#1 #2' in date range requested}\swg@ehpc \fi } % Check if result is for right event and within date range, in which case % extract variables and set \swg@eventfound true. % Called by \swg@readdata, which defines relevant variables. \def\swg@checkdata{% \expandafter\swg@parseresultline\swg@content\end %\typeout{Event \swg@fdist\space\swg@fstroke\space\swg@fcourse\space\swg@ftime} \ifx\swg@argi\swg@fdist \ifx\swg@argii\swg@fstroke % Check if date is in right range, and if so create right variables % to write to swgsi.tmp or swgli.tmp (using \swg@txdata) \expandafter\swg@date \swg@meetdate {} \ifnum\swg@daycount>\swg@startday \ifnum\swg@daycount<\swg@enddaycount \swg@matchfoundtrue \advance\swg@daycount by-\swg@startday \advance\swg@daycount by1% \expandafter\swg@tosecs\swg@ftime\relax% % CANNOT write \let\swg@ftime=\swg@timeresult on next line (why not??) \global\let\swg@xtime=\swg@timeresult \global\swg@eventfoundtrue % Reset switches that do not persist between events \gdef\swg@s@graphnote{} \fi \fi \fi \fi } % Transfer data to files to be read by PSTricks macro. % Called by \swg@readdata, which defines relevant variables \def\swg@txdata{% \if\swg@fcourse s \immediate\write1{\the\swg@daycount\space\swg@xtime} \ifx\swg@s@graphnote\@empty \immediate\write2{\swg@descmod\space\swg@meetname} \else \immediate\write2{% \swg@descmod\space\swg@meetname\space(\swg@s@graphnote)} \fi \else\if\swg@fcourse l \immediate\write3{\the\swg@daycount\space\swg@xtime} \ifx\swg@s@graphnote\@empty \immediate\write4{\swg@descmod\space\swg@meetname} \else \immediate\write4{% \swg@descmod\space\swg@meetname\space(\swg@s@graphnote)} \fi \else \swg@err{'\swg@fcourse' is not a legal course length for the event '\swg@fdist\space\swg@fstroke'^^J \space at the meet '\swg@meetname' (must be 's' or 'l')}\swg@ehpa \fi\fi \global\swg@eventfoundfalse } % If file #1.tmp exists, transfer appropriate data from it to swgtime.tmp, % read it into \swg@times, and plot it and label points if requested \def\swg@readplot#1#2{ \IfFileExists{#1.tmp}{ \readdata{\swg@times}{#1.tmp} \dataplot[plotstyle=line,linecolor=#2,linewidth=\swg@linewidth]% {\swg@times} \dataplot[plotstyle=dots,dotstyle=square*,linecolor=#2,% linewidth=\swg@linewidth]{\swg@times} \ifswg@plotlabel\swg@plotlabel{#1}\fi }{} } % Puts labels (vertically) on swimmer's graph, getting them from file % created by \swg@readdata \def\swg@plotlabel#1{ \endlinechar=-1 \openin1=#1.tmp \openin2=#1d.tmp \loop \read1 to\@templine \read2 to\swg@desc \ifx\@templine\@empty \else \expandafter\swg@parsedatline\swg@desc\end \edef\swg@desc{\swg@datargii} \if\swg@datargi u \gdef\swg@labelpos{r} \gdef\swg@sign{-} \else\if\swg@datargi R \gdef\swg@labelpos{0} \gdef\swg@sign{} \else\if\swg@datargi r \gdef\swg@labelpos{l} \gdef\swg@sign{} \fi\fi\fi % labelpos 0 means no label \if\swg@labelpos 0 \else \expandafter\swg@parsedatline\@templine\end \pssetlength{\swg@lpos}{\swg@datargii} \psaddtolength{\swg@lpos}{\swg@sign\swg@labelvsep} \rput[\swg@labelpos]{90}(\swg@datargi,\swg@lpos)% {\color{\swg@labelcolor}\swg@labelfontsize\swg@desc} \fi \fi \ifeof1\relax\swg@continuefalse\else\swg@continuetrue\fi \ifswg@continue \repeat \closein1 \closein2 \endlinechar=13 } \def\swg@back{back} \def\swg@breast{breast} \def\swg@fly{fly} \def\swg@free{free} \def\swg@IM{IM} \def\swg@FR{FR} % free relay \def\swg@MR{MR} % medley relay %-------------------------------------% % SWIMGRAPH % %-------------------------------------% % % \swimgraph[options]{distance stroke}{startdate}{enddate}{lowtime}{hightime}% % #1 #2 #3 #4 #5 % options: any legal arguments for \psset % distance: 50, 100, 200, 400, 800, 1500, 4x50, 4x100 % stroke: breast, fly, free, back, IM, FR, MR % startdate: year-month-day % enddate: year-month-day % lowtime: lowest time for y axis, in seconds or minutes:seconds % hightime: highest time for y axis, in seconds or minutes:seconds \define@key{swimgraph}{xunit}{\psset{xunit=#1}} \define@key{swimgraph}{yunit}{\psset{yunit=#1}} \define@key{swimgraph}{framesep}{\psset{framesep=#1}} \define@key{swimgraph}{nolabels}[true]{\swg@plotlabelfalse} \define@key{swimgraph}{datafile}{\def\swg@datafile{#1}} \define@key{swimgraph}{recordpath}{\def\swg@filepath{#1}} \define@key{swimgraph}{wsccolor}{\def\swg@wsccolor{#1}} \define@key{swimgraph}{wlccolor}{\def\swg@wlccolor{#1}} \define@key{swimgraph}{xsccolor}{\def\swg@xsccolor{#1}} \define@key{swimgraph}{xlccolor}{\def\swg@xlccolor{#1}} \define@key{swimgraph}{ysccolor}{\def\swg@ysccolor{#1}} \define@key{swimgraph}{ylccolor}{\def\swg@ylccolor{#1}} \define@key{swimgraph}{zsccolor}{\def\swg@zsccolor{#1}} \define@key{swimgraph}{zlccolor}{\def\swg@zlccolor{#1}} \define@key{swimgraph}{jsccolor}{\def\swg@jsccolor{#1}} \define@key{swimgraph}{jlccolor}{\def\swg@jlccolor{#1}} \define@key{swimgraph}{nsccolor}{\def\swg@nsccolor{#1}} \define@key{swimgraph}{nlccolor}{\def\swg@nlccolor{#1}} \define@key{swimgraph}{asccolor}{\def\swg@asccolor{#1}} \define@key{swimgraph}{alccolor}{\def\swg@alccolor{#1}} \define@key{swimgraph}{bsccolor}{\def\swg@bsccolor{#1}} \define@key{swimgraph}{blccolor}{\def\swg@blccolor{#1}} \define@key{swimgraph}{csccolor}{\def\swg@csccolor{#1}} \define@key{swimgraph}{clccolor}{\def\swg@clccolor{#1}} \define@key{swimgraph}{dsccolor}{\def\swg@dsccolor{#1}} \define@key{swimgraph}{dlccolor}{\def\swg@dlccolor{#1}} \define@key{swimgraph}{esccolor}{\def\swg@esccolor{#1}} \define@key{swimgraph}{elccolor}{\def\swg@elccolor{#1}} \define@key{swimgraph}{fsccolor}{\def\swg@rsccolor{#1}} \define@key{swimgraph}{flccolor}{\def\swg@flccolor{#1}} \define@key{swimgraph}{sccolor}{\def\swg@sccolor{#1}} \define@key{swimgraph}{lccolor}{\def\swg@lccolor{#1}} \define@key{swimgraph}{labelcolor}{\def\swg@labelcolor{#1}} \define@key{swimgraph}{monthlinecolor}{\def\swg@monthlinecolor{#1}} \define@key{swimgraph}{yearlinecolor}{\def\swg@yearlinecolor{#1}} \define@key{swimgraph}{oneseclinecolor}{\def\swg@oneseclinecolor{#1}} \define@key{swimgraph}{fiveseclinecolor}{\def\swg@fiveseclinecolor{#1}} \define@key{swimgraph}{labelfontsize}{\def\swg@labelfontsize{#1}} \define@key{swimgraph}{keyvsep}{\def\swg@keyvsep{#1}} \define@key{swimgraph}{monthvsep}{\def\swg@monthvsep{#1}} \define@key{swimgraph}{yearvsep}{\def\swg@yearvsep{#1}} \define@key{swimgraph}{titlevsep}{\def\swg@titlevsep{#1}} \define@key{swimgraph}{labelvsep}{\def\swg@labelvsep{#1}} \define@key{swimgraph}{tlabelpos}{\def\swg@tlabelpos{#1}} \define@key{swimgraph}{linewidth}{\def\swg@linewidth{#1}} \define@key{swimgraph}{reclinewidth}{\def\swg@reclinewidth{#1}} \pagestyle{empty} \def\swimgraph{% \@ifnextchar[{\swg@opt}{\swg@noopt}} \def\swg@opt[#1]#2#3#4#5#6{% % \psset{#1} \setkeys{swimgraph}{#1} % set unit equal to yunit (so that \pssetlength uses psyunit---used to % position labels and titles). \let\psunit=\psyunit \swg@main{#2}{#3}{#4}{#5}{#6}} \def\swg@noopt#1#2#3#4#5{\swg@main{#1}{#2}{#3}{#4}{#5}} \def\swg@main#1#2#3#4#5{% % default: short course \def\swg@fcourse{s} \def\swg@s@graphnote{} % %.....................................% % Basic set up % %.....................................% % % start date \swg@date #2 \swg@age=\swg@year \swg@startday=\swg@daycount \swg@startdiy=\swg@diycount \swg@startyear=\swg@year % % end date \swg@date #3 \swg@endage=\swg@year \swg@endday=\swg@daycount \swg@enddaycount=\swg@daycount \swg@enddiy=\swg@diycount \advance\swg@endday by-\swg@startday %\typeout{Endday: \the\swg@endday} % \ifnum\swg@startday>\swg@enddaycount \swg@err{Starting date is after ending date}\swg@ehpc \fi % open data file and get swimmer's name and date of birth % (for use with records) \openin1=\swg@datafile \endlinechar=-1 % first line is comment---ignored \read1 to\swg@fileline % second line is swimmer's name \read1 to\swg@name % third line is swimmer's name \read1 to\swg@sex % fourth line is swimmer's date of birth \read1 to\swg@dob \endlinechar=13 \closein1 \typeout{Swimmer's name: \swg@name. Date of birth: \swg@dob.} % swimmer's birthdate \expandafter\swg@date \swg@dob {} \swg@bdaydiy=\swg@diycount \def\swg@bday{\the\swg@diycount} \advance\swg@age by-\swg@year \ifnum\swg@startdiy<\swg@diycount\advance\swg@age by-1\fi \advance\swg@endage by-\swg@year \ifnum\swg@enddiy<\swg@diycount\advance\swg@endage by-1\fi \swg@parsedatline#1\end \let\swg@dist=\swg@datargi \let\@@swimstroke=\swg@datargii \ifx\@@swimstroke\swg@breast\def\swg@stroke{breast stroke}% \def\swg@strokex{ breast} \else\ifx\@@swimstroke\swg@fly\def\swg@stroke{butterfly}% \def\swg@strokex{ fly} \else\ifx\@@swimstroke\swg@free\def\swg@stroke{freestyle}% \def\swg@strokex{ free} \else\ifx\@@swimstroke\swg@back\def\swg@stroke{backstroke}% \def\swg@strokex{ back} \else\ifx\@@swimstroke\swg@IM\def\swg@stroke{IM}% \def\swg@strokex{ IM} \else\ifx\@@swimstroke\swg@FR\def\swg@stroke{Free Relay}% \def\swg@strokex{ FR} \else\ifx\@@swimstroke\swg@MR\def\swg@stroke{Medley Relay}% \def\swg@strokex{ MR} \else \swg@err{What stroke is '\@@swimstroke'?\space\space I don't know it}\swg@ehpc \fi\fi\fi\fi\fi\fi\fi \def\swg@t{#4} \expandafter\swg@tosecso\swg@t\relax \swg@ltime=\swg@timeresult \def\swg@t{#5} \expandafter\swg@tosecso\swg@t\relax \swg@htime=\swg@timeresult \pssetlength{\swg@monthbaseh}{\the\swg@ltime\psyunit} \psaddtolength{\swg@monthbaseh}{-\swg@monthvsep} % number of light lines \swg@lghlines=\swg@htime \advance\swg@lghlines by-\swg@ltime % number of heavy lines (5 second intervals) \swg@hvylines=\swg@lghlines \divide\swg@hvylines by5 \swg@midy=\swg@htime \advance\swg@midy by\swg@ltime \divide\swg@midy by2 %.....................................% % Beginning of \pspicture % %.....................................% \pssetlength{\swg@top}{\swg@htime} \psaddtolength{\swg@top}{\swg@keyvsep} % \swg@top isn't quite right---equal to middle height of key, so add a bit \psaddtolength{\swg@top}{1.6mm} \pssetlength{\swg@bottom}{\swg@ltime} \psaddtolength{\swg@bottom}{-\swg@monthvsep} \psaddtolength{\swg@bottom}{-\swg@yearvsep} \psaddtolength{\swg@bottom}{-\swg@titlevsep} \vspace*{\fill} \begin{center} % \fbox for debugging size of picture % \fboxsep 0pt % \fbox{ \begin{pspicture}(-\swg@tlabelpos,\swg@bottom)(\swg@endday,\swg@top) %.....................................% % Horizontal lines on graph and axes % %.....................................% % Lines every second \multido{\i=\swg@ltime+1}{\swg@lghlines}% {\psline[linecolor=\swg@oneseclinecolor,linewidth=0.4pt]% (0,\i)(\swg@endday,\i)} % Lines at multiples of 5 second \swg@lhvytime=\swg@ltime \divide\swg@lhvytime by 5 \multiply\swg@lhvytime by 5 \advance\swg@lhvytime by 5 \multido{\i=\swg@lhvytime+5}{\swg@hvylines}% {\psline[linecolor=\swg@fiveseclinecolor]% (0,\i)(\swg@endday,\i)} % Axes \psaxes[axesstyle=frame,Dx=50,Oy=\the\swg@lhvytime,Dy=5,% tickstyle=bottom,labels=y,ticks=y]% (0.5,\swg@lhvytime)(0.5,\swg@ltime)(\swg@endday,\swg@htime) % Because of the offset in \psaxes, so that it starts numbering at the % right point, we won't get a line at the lowest time, so put one \psline(\swg@endday,\swg@htime)(\swg@endday,\swg@ltime)% (0.5,\swg@ltime)(0.5,\swg@htime) %...................................................................% % Vertical lines indicating month divisions; month and year labels % %...................................................................% % Arguments of \swg@putmonth: month name, # of days in month, % # of days in year to 1st day of month. % Goes through loop till December, but \swg@putmonth does something only % if endday hasn't been reached. \@tempcntb=\swg@startdiy \@tempcntc=0 % x-coordinate \@tempcntx=0 % x-coordinate of beginning of year \@tempcntd=\swg@startday \@tempcntyr=\swg@startyear % If startdiy > 335, no month label (not even Dec) will be printed, so first % year printed will be next year \ifnum\@tempcntb>335\advance\@tempcntyr by1\fi \loop \ifnum\@tempcntb<2\swg@putmonth{Jan}{31}{0}\fi \ifnum\@tempcntb<33\swg@putmonth{Feb}{28}{31}\fi \ifnum\@tempcntb<61\swg@putmonth{Mar}{31}{59}\fi \ifnum\@tempcntb<92\swg@putmonth{Apr}{30}{90}\fi \ifnum\@tempcntb<122\swg@putmonth{May}{31}{120}\fi \ifnum\@tempcntb<153\swg@putmonth{Jun}{30}{151}\fi \ifnum\@tempcntb<183\swg@putmonth{Jul}{31}{181}\fi \ifnum\@tempcntb<214\swg@putmonth{Aug}{31}{212}\fi \ifnum\@tempcntb<245\swg@putmonth{Sep}{30}{243}\fi \ifnum\@tempcntb<275\swg@putmonth{Oct}{31}{273}\fi \ifnum\@tempcntb<306\swg@putmonth{Nov}{30}{304}\fi \ifnum\@tempcntb<336\swg@putmonth{Dec}{31}{334}\fi \ifnum\@tempcntb<367\swg@putmonth{}{31}{365} % Year label \@tempcnty=\@tempcntc \swg@mid=\@tempcnty\advance\swg@mid by\@tempcntx \divide\swg@mid by2 \@tempcntz=\@tempcnty\advance\@tempcntz by-\@tempcntx % put yr label only if at least 30 days of yr present and x=0 or at least % 32 days of year present (case of Jan at end of plot) \ifnum\@tempcntz>29\ifnum\@tempcntx=0\relax\swg@putyrtrue\fi\fi \ifnum\@tempcntz>31\relax\swg@putyrtrue\fi \ifswg@putyr \pssetlength{\swg@labelh}{\swg@monthbaseh} \psaddtolength{\swg@labelh}{-\swg@yearvsep} \rput[B](\swg@mid,\swg@labelh){\textbf{\the\@tempcntyr}} \fi \swg@putyrfalse \advance\@tempcntyr by 1\fi \ifnum\@tempcntd<\swg@enddaycount \@tempcntx=\@tempcnty \advance\@tempcntb by-365 \repeat %..........% % Records % %..........% \typeout{Event: \swg@dist\swg@strokex} % Age-independent standards/records % National record \ifswg@n\swg@recline{n}{\swg@dist\swg@strokex}{0}{\the\swg@endday}\fi % World record \ifswg@w\swg@recline{w}{\swg@dist\swg@strokex}{0}{\the\swg@endday}\fi % Other records \ifswg@j\swg@recline{j}{\swg@dist\swg@strokex}{0}{\the\swg@endday}\fi \ifswg@x\swg@recline{x}{\swg@dist\swg@strokex}{0}{\the\swg@endday}\fi \ifswg@y\swg@recline{y}{\swg@dist\swg@strokex}{0}{\the\swg@endday}\fi \ifswg@z\swg@recline{z}{\swg@dist\swg@strokex}{0}{\the\swg@endday}\fi % Age-dependent standards/records \ifswg@a\swg@agerectrue\swg@agerecline{a}\fi \ifswg@b\swg@agerectrue\swg@agerecline{b}\fi \ifswg@c\swg@agerectrue\swg@agerecline{c}\fi \ifswg@d\swg@agerectrue\swg@agerecline{d}\fi \ifswg@e\swg@agerectrue\swg@agerecline{e}\fi \ifswg@f\swg@agerectrue\swg@agerecline{f}\fi \swg@agerecfalse %...........................% % Graph of swimmer's times % %...........................% % Read swimmer's times from file \swg@readdata{\swg@dist}{\@@swimstroke} % Plot times \swg@i=1 \loop \swg@readplot{swgs\the\swg@i}{\swg@sccolor} \swg@readplot{swgl\the\swg@i}{\swg@lccolor} \ifnum\swg@i<4\advance\swg@i by1 \repeat %..........................................% % Key at top and labels at side and bottom % %..........................................% \divide\swg@endday by2 \pssetlength{\swg@keyh}{\the\swg@htime\psyunit} \addtolength{\swg@keyh}{\swg@keyvsep} % key giving SC and LC colors---on top of graph \def\swg@key{ \psline[linecolor=\swg@sccolor,linewidth=\swg@linewidth](-45,0)(-25,0) \rput*[l](-23,0){SC} \psline[linecolor=\swg@lccolor,linewidth=\swg@linewidth](7,0)(27,0) \rput*[l](29,0){LC} } \rput(\swg@endday,\swg@keyh){\swg@key} % label at side \rput{90}(-\swg@tlabelpos,\swg@midy){time} % label at bottom \psaddtolength{\swg@labelh}{-\swg@titlevsep} \rput[B](\the\swg@endday,\swg@labelh)% {\large\textbf{\swg@name's times for {\swg@dist}m \swg@stroke}} \end{pspicture} % } % end of \fbox \end{center} \vspace*{\fill} } %-------------------------------------% % SWIMTEXT % %-------------------------------------% % Formats details of result (giving winning time, winner, etc. as specified % in data file \def\swg@makeresult{ % convert times to mins:secs.msecs. \expandafter\swg@tominssecs\swg@ftime\relax % CANNOT write \let\swg@ftime=\swg@timeresult on next line (why not??) \let\swg@xtime=\swg@timeresult % start of item \item[\swg@fdist m \swg@fstroke \ifx\swg@s@age\@empty\else\ (\swg@s@age)\fi \if\swg@fcourse s (S): \else\if\swg@fcourse l (L): \else \typeout{Note: '\swg@fcourse' is unknown course length (must be 's' or 'l'} \fi\fi ] \swg@xtime \ifx\swg@s@place\@empty \else \ \ -\ \ \swg@s@place \fi \ifx\swg@s@ageplace\@empty \else \ (\swg@s@ageplace) \fi \ifx\swg@s@type\@empty \else \par \swg@s@type \fi \ifx\swg@s@wintime\@empty \else \par Winning time: \swg@s@wintime. \fi \ifx\swg@s@first\@empty \else \par First: \swg@s@first. \fi \ifx\swg@s@second\@empty \else \ifx\swg@s@first\@empty \par \fi Second: \swg@s@second. \fi \ifx\swg@s@third\@empty \else \ifx\swg@s@first\@empty \ifx\swg@s@second\@empty \par \fi\fi Third: \swg@s@third. \fi \ifx\swg@s@fourth\@empty \else \ifx\swg@s@first\@empty \ifx\swg@s@second\@empty \ifx\swg@s@third\@empty \par \fi\fi\fi Fourth: \swg@s@fourth. \fi \ifx\swg@s@other\@empty \else \par \swg@s@other \fi% \global\swg@eventfoundfalse } % Output any pending result and get new result details. % Called by \swimtext. \def\swg@eventx{% % Output any accumulated result \ifswg@eventfound\swg@makeresult\fi \expandafter\swg@parseresultline\swg@content\end \global\swg@eventfoundtrue \gdef\swg@s@place{} \gdef\swg@s@ageplace{} \gdef\swg@s@wintime{} \gdef\swg@s@first{} \gdef\swg@s@second{} \gdef\swg@s@third{} \gdef\swg@s@fourth{} \gdef\swg@s@type{} \gdef\swg@s@other{} } \define@key{swimtext}{datafile}{\def\swg@datafile{#1}} % Main text macro, which produces a document using the information in the % data file. \def\swimtext{% \@ifnextchar[{\swg@textopt}{\swg@textnoopt}} \def\swg@textopt[#1]#2#3{% \setkeys{swimtext}{#1} \swg@textmain{#2}{#3}} \def\swg@textnoopt#1#2{\swg@textmain{#1}{#2}} \def\swg@textmain#1#2{% % default: short course \def\swg@fcourse{s} \def\swg@s@age{} % start date \swg@date #1 \swg@age=\swg@year \swg@startday=\swg@daycount \swg@startdiy=\swg@diycount \swg@startyear=\swg@year % end date \swg@date #2 \swg@endage=\swg@year \swg@endday=\swg@daycount \swg@enddaycount=\swg@daycount \swg@enddiy=\swg@diycount \advance\swg@endday by-\swg@startday \ifnum\swg@startday>\swg@enddaycount \swg@err{Starting date is after ending date}\swg@ehpc \fi \endlinechar=-1 \openin0=\swg@datafile % first line is comment \read0 to\swg@fileline \read0 to\swg@name \read0 to\swg@sex \read0 to\swg@dob \begin{center}\large\textbf{\swg@name's times at meets between\\#1 and #2}\end{center} \global\swg@eventfoundfalse \loop \read0 to\swg@fileline % Next line necessary because blank line is read at end of file, before % \ifeof0 is true. It also allows all lines starting % to be ignored. \ifx\swg@fileline\@empty \else \expandafter\swg@parsedataline\swg@fileline\end \if\swg@switch m % Get meet name and store it. Don't output anything yet, because don't % know if date is in range---output occurs after date is read. \global\let\swg@meetname=\swg@content \global\swg@nmntrue \else\if\swg@switch M \global\let\swg@meetname=\swg@content \else\if\swg@switch d \global\let\swg@meetdate=\swg@content %\typeout{Date: \swg@meetdate} \expandafter\swg@date \swg@meetdate {} \swg@inrangefalse % till proved otherwise \global\swg@nmdtrue % If date is in right range, continue \ifnum\swg@daycount>\swg@startday \ifnum\swg@daycount<\swg@enddaycount \global\swg@inrangetrue \ifswg@nmn % Output any accumulated result \ifswg@eventfound\swg@makeresult\fi \ifswg@indescription \end{swgdescription} \global\swg@indescriptionfalse \fi \section*{\swg@meetname}\swg@nmnfalse \fi \ifswg@nmd \ifswg@eventfound\swg@makeresult\fi \ifswg@indescription \end{swgdescription} \global\swg@indescriptionfalse \fi \subsection*{\swg@meetdate}\swg@nmdfalse \fi \begin{swgdescription} \global\swg@indescriptiontrue \fi \else\typeout{Date \swg@meetdate\space ignored because out of requested range} \fi \else\if\swg@switch r \ifswg@inrange\swg@eventx\fi \else\if\swg@switch u \ifswg@inrange\swg@eventx\fi \else\if\swg@switch R \ifswg@inrange\swg@eventx\fi \else\if\swg@switch x \ifswg@inrange\swg@eventx\fi \else\if\swg@switch a \global\let\swg@s@age=\swg@content \else\if\swg@switch c \global\let\swg@fcourse=\swg@content \else\if\swg@switch o \let\swg@s@other=\swg@content \else\if\swg@switch t \let\swg@s@type=\swg@content \else\if\swg@switch p \let\swg@s@place=\swg@content \else\if\swg@switch P \let\swg@s@ageplace=\swg@content \else\if\swg@switch w \let\swg@s@wintime=\swg@content \else\if\swg@switch 1 \let\swg@s@first=\swg@content \else\if\swg@switch 2 \let\swg@s@second=\swg@content \else\if\swg@switch 3 \let\swg@s@third=\swg@content \else\if\swg@switch 4 \let\swg@s@fourth=\swg@content \fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi \fi \ifeof0\relax\swg@continuefalse\else\swg@continuetrue\fi \ifswg@continue% \repeat% % Output any accumulated result \ifswg@eventfound\swg@makeresult\fi \ifswg@indescription \end{swgdescription} \fi \endlinechar=13 \closein0 }