diff options
| author | Douglas B. Rumbaugh <doug@douglasrumbaugh.com> | 2026-01-11 12:19:10 -0500 |
|---|---|---|
| committer | Douglas B. Rumbaugh <doug@douglasrumbaugh.com> | 2026-01-11 12:19:10 -0500 |
| commit | f5f69a70c25af4f59f9caa5f0d24e46b7fbd9871 (patch) | |
| tree | fefebb89aa66a104ac15656f5c4442b13a506ca0 | |
| parent | 85820053d41868bff568978cb0af492a69f40a12 (diff) | |
| download | weekschedule-f5f69a70c25af4f59f9caa5f0d24e46b7fbd9871.tar.gz | |
Initial commit of schedule class
| -rw-r--r-- | weekschedule.cls | 257 |
1 files changed, 257 insertions, 0 deletions
diff --git a/weekschedule.cls b/weekschedule.cls new file mode 100644 index 0000000..7fccb80 --- /dev/null +++ b/weekschedule.cls @@ -0,0 +1,257 @@ +% weekschedule.cls - A LaTeX class for creating weekly schedule calendars +% Usage: +% \documentclass{weekschedule} +% \scheduletitle{Weekly Schedule - Spring 2026} +% \scheduleauthor{Dr. Smith} +% \timefrom{8:00} +% \timeto{18:00} +% \twelvehourtime % or \twentyfourhourtime +% \eventclass{Courses}{173,216,230} % RGB values +% \event{Courses}{MATH 101}{Monday,Wednesday,Friday}{9:00}{10:00} +% \begin{document} +% \printschedule +% \end{document} + +\NeedsTeXFormat{LaTeX2e} +\ProvidesClass{weekschedule}[2026/01/11 Weekly Schedule Class] + +% Load base class +\LoadClass[landscape,11pt]{article} + +% Required packages +\RequirePackage[margin=0.5in]{geometry} +\RequirePackage{tikz} +\RequirePackage{xcolor} +\RequirePackage{pgfmath} + +\pagestyle{empty} + +% ============================================ +% Configuration variables with defaults +% ============================================ +\newcommand{\@scheduletitle}{Weekly Schedule} +\newcommand{\@scheduleauthor}{} +\newcommand{\@starthour}{8} +\newcommand{\@endhour}{18} +\newif\if@usetwentyfourhour +\@usetwentyfourhourfalse + +% Layout parameters +\newcommand{\@daywidth}{4.5} +\newcommand{\@hourheight}{1.5} +\newcommand{\@padding}{0.08} + +% ============================================ +% User-facing configuration commands +% ============================================ +\newcommand{\scheduletitle}[1]{\renewcommand{\@scheduletitle}{#1}} +\newcommand{\scheduleauthor}[1]{\renewcommand{\@scheduleauthor}{#1}} + +% Parse time string like "8:00" or "13:30" and store hour +\newcommand{\timefrom}[1]{% + \@parsetime{#1}% + \pgfmathtruncatemacro{\@temph}{\@parsedtime}% + \edef\@starthour{\@temph}% +} + +\newcommand{\timeto}[1]{% + \@parsetime{#1}% + \pgfmathtruncatemacro{\@temph}{\@parsedtime}% + \edef\@endhour{\@temph}% +} + +% Time format toggles +\newcommand{\twentyfourhourtime}{\@usetwentyfourhourtrue} +\newcommand{\twelvehourtime}{\@usetwentyfourhourfalse} + +% Layout customization +\newcommand{\setdaywidth}[1]{\renewcommand{\@daywidth}{#1}} +\newcommand{\sethourheight}[1]{\renewcommand{\@hourheight}{#1}} +\newcommand{\setpadding}[1]{\renewcommand{\@padding}{#1}} + +% ============================================ +% Internal time parsing +% ============================================ +% Parse time to decimal (e.g., "9:30" -> 9.5) +\def\@parsetime#1{\@@parsetime#1\@nil} +\def\@@parsetime#1:#2\@nil{% + \pgfmathparse{#1 + #2/60}% + \edef\@parsedtime{\pgfmathresult}% +} + +% ============================================ +% Event class definitions (colors) +% ============================================ +\newcounter{@numclasses} +\setcounter{@numclasses}{0} + +% Define an event class with RGB color +% Usage: \eventclass{ClassName}{R,G,B} +\newcommand{\eventclass}[2]{% + \definecolor{class@#1}{RGB}{#2}% + \stepcounter{@numclasses}% + \expandafter\def\csname @classname\the@numclasses\endcsname{#1}% +} + +% ============================================ +% Event storage +% ============================================ +\newcounter{@numevents} +\setcounter{@numevents}{0} + +% Store an event +% Usage: \event{ClassName}{Event Name}{Days}{Start}{End} +% Days can be: Monday, Tuesday, Wednesday, Thursday, Friday (comma-separated) +\newcommand{\event}[5]{% + \stepcounter{@numevents}% + \expandafter\def\csname @eventclass\the@numevents\endcsname{#1}% + \expandafter\def\csname @eventname\the@numevents\endcsname{#2}% + \expandafter\def\csname @eventdays\the@numevents\endcsname{#3}% + \expandafter\def\csname @eventstart\the@numevents\endcsname{#4}% + \expandafter\def\csname @eventend\the@numevents\endcsname{#5}% +} + +% ============================================ +% Main schedule rendering +% ============================================ +\newcommand{\printschedule}{% + % Title and author + \begin{center} + \LARGE\textbf{\@scheduletitle}% + \ifx\@scheduleauthor\empty\else\\[0.2cm]\Large\@scheduleauthor\fi% + \end{center} + \vspace{0.05cm} + + \begin{tikzpicture}[x=1cm, y=1cm] + % Store parameters + \pgfmathsetmacro{\daywidth}{\@daywidth} + \pgfmathsetmacro{\hourheight}{\@hourheight} + \pgfmathsetmacro{\padding}{\@padding} + \pgfmathtruncatemacro{\starthour}{\@starthour} + \pgfmathtruncatemacro{\endhour}{\@endhour} + + % Draw column headers (days) + \foreach \day/\dayname in {0/Monday, 1/Tuesday, 2/Wednesday, 3/Thursday, 4/Friday} { + \node[anchor=south, font=\large\bfseries] at (\day*\daywidth+\daywidth/2, -\starthour*\hourheight+0.2) {\dayname}; + } + + % Draw row headers (times) + \foreach \hour in {\starthour,...,\endhour} { + \pgfmathtruncatemacro{\displayhour}{mod(\hour-1,12)+1} + \if@usetwentyfourhour + \node[anchor=east, font=\small] at (-0.1, -\hour*\hourheight) {\hour:00}; + \else + \ifnum\hour<12 + \def\@ampm{AM} + \else + \def\@ampm{PM} + \fi + \node[anchor=east, font=\small] at (-0.1, -\hour*\hourheight) {\displayhour:00 \@ampm}; + \fi + } + + % Draw vertical lines (day separators) + \foreach \day in {0,...,5} { + \draw[gray] (\day*\daywidth, -\starthour*\hourheight) -- (\day*\daywidth, -\endhour*\hourheight); + } + + % Draw horizontal lines (hour separators) + \foreach \hour in {\starthour,...,\endhour} { + \draw[gray] (0, -\hour*\hourheight) -- (5*\daywidth, -\hour*\hourheight); + } + + % Draw half-hour lines (dashed) + \pgfmathtruncatemacro{\lasthour}{\endhour-1} + \foreach \hour in {\starthour,...,\lasthour} { + \draw[gray, dashed, line width=0.25pt] (0, -\hour*\hourheight-0.5*\hourheight) -- (5*\daywidth, -\hour*\hourheight-0.5*\hourheight); + } + + % Draw all events + \foreach \evnum in {1,...,\the@numevents} { + \@drawevent{\evnum} + } + + \end{tikzpicture} + + \vspace{0.2cm} + + % Legend + \begin{center} + \begin{tikzpicture} + \foreach \clnum in {1,...,\the@numclasses} { + \pgfmathsetmacro{\xpos}{(\clnum-1)*3.5} + \edef\@clname{\csname @classname\clnum\endcsname} + \fill[class@\@clname] (\xpos,0) rectangle (\xpos+0.4,0.3); + \node[anchor=west] at (\xpos+0.5,0.15) {\@clname}; + } + \end{tikzpicture} + \end{center} +} + +% ============================================ +% Event drawing helper +% ============================================ +\newcommand{\@drawevent}[1]{% + \edef\@evclass{\csname @eventclass#1\endcsname}% + \edef\@evname{\csname @eventname#1\endcsname}% + \edef\@evdays{\csname @eventdays#1\endcsname}% + \edef\@evstart{\csname @eventstart#1\endcsname}% + \edef\@evend{\csname @eventend#1\endcsname}% + % Parse times + \expandafter\@parsetime\expandafter{\@evstart}% + \let\@startdec\@parsedtime + \expandafter\@parsetime\expandafter{\@evend}% + \let\@enddec\@parsedtime + % Draw for each day + \@drawforeachday{\@evclass}{\@evname}{\@evdays}{\@startdec}{\@enddec}% +} + +\newcommand{\@drawforeachday}[5]{% + % #1=class, #2=name, #3=days, #4=start, #5=end + \@checkandrawday{#1}{#2}{#3}{#4}{#5}{Monday}{0}% + \@checkandrawday{#1}{#2}{#3}{#4}{#5}{Tuesday}{1}% + \@checkandrawday{#1}{#2}{#3}{#4}{#5}{Wednesday}{2}% + \@checkandrawday{#1}{#2}{#3}{#4}{#5}{Thursday}{3}% + \@checkandrawday{#1}{#2}{#3}{#4}{#5}{Friday}{4}% +} + +\newcommand{\@checkandrawday}[7]{% + % #1=class, #2=name, #3=days string, #4=start, #5=end, #6=dayname, #7=daynum + \@checkdayinlist{#3}{#6}% + \if@daymatched + \@draweventbox{#1}{#2}{#7}{#4}{#5}% + \fi +} + +\newif\if@daymatched + +\newcommand{\@checkdayinlist}[2]{% + % #1 = comma-separated day list, #2 = day to check + \@daymatchedfalse + \def\@daytocheck{#2}% + \@for\@testday:=#1\do{% + \edef\@trimmedday{\@testday}% + \ifx\@trimmedday\@daytocheck + \@daymatchedtrue + \fi + }% +} + +\newcommand{\@draweventbox}[5]{% + % #1 = class, #2 = name, #3 = day number, #4 = start decimal, #5 = end decimal + \pgfmathsetmacro{\daywidth}{\@daywidth} + \pgfmathsetmacro{\hourheight}{\@hourheight} + \pgfmathsetmacro{\padding}{\@padding} + \pgfmathsetmacro{\xstart}{#3*\daywidth+\padding} + \pgfmathsetmacro{\xend}{#3*\daywidth+\daywidth-\padding} + \pgfmathsetmacro{\ystart}{-#4*\hourheight-\padding} + \pgfmathsetmacro{\yend}{-#5*\hourheight+\padding} + \pgfmathsetmacro{\ymid}{(\ystart+\yend)/2} + \pgfmathsetmacro{\xcenter}{#3*\daywidth+\daywidth/2} + % Draw the box + \fill[class@#1, rounded corners] (\xstart, \ystart) rectangle (\xend, \yend); + % Draw the label + \node[font=\small, align=center] at (\xcenter, \ymid) {#2}; +} + +\endinput |