From cbd1673e680c2f55eb60458dd4ecbb5c2060838f Mon Sep 17 00:00:00 2001
From: Loek Le Blansch <loek@pipeframe.xyz>
Date: Fri, 13 Sep 2024 16:06:41 +0200
Subject: hide glossary if no glossary entries used

---
 projdoc.cls | 14 +++++++++-----
 1 file changed, 9 insertions(+), 5 deletions(-)

(limited to 'projdoc.cls')

diff --git a/projdoc.cls b/projdoc.cls
index 388f901..cc25e67 100644
--- a/projdoc.cls
+++ b/projdoc.cls
@@ -206,19 +206,23 @@
 	\thispagestyle{empty}
 	\end{titlepage}
 }
-\newbool{projdoc@cited}
-\apptocmd{\abx@aux@cite}{\global\booltrue{projdoc@cited}}{}{}
+\newbool{projdoc@used@cite}
+\newbool{projdoc@used@gls}
+\apptocmd{\abx@aux@cite}{\global\booltrue{projdoc@used@cite}}{}{}
+\apptocmd{\glsxtr@record}{\global\booltrue{projdoc@used@gls}}{}{}
 \def\projdoc@trailer{
 	% bibliography (if citations used)
-	\ifbool{projdoc@cited}{\printbibliography}{}%
+	\ifbool{projdoc@used@cite}{%
+		\printbibliography%
+	}{}%
 	% glossary
-	\ifcsundef{printunsrtglossary}{}{%
+	\ifbool{projdoc@used@cite}{%
 		\section*{Glossary}%
 		\begin{multicols}{2}%
 			\renewcommand{\glossarysection}[2][]{}%
 			\printunsrtglossary%
 		\end{multicols}%
-	}%
+	}{}%
 }
 \AtBeginDocument{\projdoc@header}
 \AtEndDocument{\projdoc@trailer}
-- 
cgit v1.2.3


From 454b9e222473c67d0b3bb6935bf3bfb8d2e2043d Mon Sep 17 00:00:00 2001
From: Loek Le Blansch <loek@pipeframe.xyz>
Date: Fri, 13 Sep 2024 16:40:50 +0200
Subject: merge #10

---
 plan.tex    | 276 ++++++++++++++++++++++++++++++++++++++++--------------------
 projdoc.cls |  18 +++-
 sources.bib |  10 +--
 3 files changed, 205 insertions(+), 99 deletions(-)

(limited to 'projdoc.cls')

diff --git a/plan.tex b/plan.tex
index 2274c87..076ce11 100644
--- a/plan.tex
+++ b/plan.tex
@@ -5,132 +5,200 @@
 % Version 0.0 seems not right
 % I'm also missing a 'version table'
 % Shall we use this version system?:
-% Version numbers after the comma indicate an interim version and version numbers before the comma indicate versions ready for publication
+% Version numbers after the comma indicate an interim version and version numbers
+% before the comma indicate versions ready for publication
 
 \title{Project Plan}
 \version{0.1}
 
 \begin{document}
-
 \tablestables
 \newpage
 
+\section{Background}
 
-\section {Background}
-This project is part of the fourth-year minor `Systems Programming in C++'. The minor consists of several courses troughout 20 weeks, which create the basis for this project. The project is focussed on building a game engine using the programming language C++. This document describes our plan of attack for the project.
+This project is part of the fourth-year minor `Systems Programming in C++'. The minor
+consists of several courses troughout 20 weeks, which create the basis for this
+project. The project is focussed on building a game engine using the programming
+language C++. This document describes our plan of attack for the project.
 
 \section{Problem Definition}
-% The assignments will be discussed with the project supervisor on specifics for this project. This will give the team the ability to direct the project.
-% @Jaro: Ik snap niet wat je met deze zinnen wil zeggen. Het lijkt een soort van background information, maar toch niet helemaal ofzo. Hoe dan ook heb ik het er nu even uit gehaald en een nieuw kopje background toegevoegd.
+
+% The assignments will be discussed with the project supervisor on specifics for this
+% project. This will give the team the ability to direct the project.
+
+% @Jaro: Ik snap niet wat je met deze zinnen wil zeggen. Het lijkt een soort van
+% background information, maar toch niet helemaal ofzo. Hoe dan ook heb ik het er nu
+% even uit gehaald en een nieuw kopje background toegevoegd.
 
 \subsection{Problem Analysis}
-CodedFun Games is a small, single-person game company looking to scale up. The owner, who is also a game programmer, and graphical artist, has received government funding, which he wants to invest in a custom-built game engine. The owner has no interest in developing or maintaining a game engine himself, so he has hired a part-time engine programmer. This programmer does not have time to create an entire engine but is willing to maintain or expand an existing engine one day per week.
 
-The client seeks a custom game engine that is easy to maintain, easy to extend, and user-friendly. Additionally, the engine should be well-documented, which is considered an essential aspect of being user-friendly.
+CodedFun Games is a small, single-person game company looking to scale up. The owner,
+who is also a game programmer, and graphical artist, has received government funding,
+which he wants to invest in a custom-built game engine. The owner has no interest in
+developing or maintaining a game engine himself, so he has hired a part-time engine
+programmer. This programmer does not have time to create an entire engine but is
+willing to maintain or expand an existing engine one day per week.
 
-So far, the client has made all his games in Unity and is very fond of the structure. Therefore, he wants the new engine to adhere to a similar structure. A simple requirements document is defined to specify how strictly this structure should be followed.
+The client seeks a custom game engine that is easy to maintain, easy to extend, and
+user-friendly. Additionally, the engine should be well-documented, which is
+considered an essential aspect of being user-friendly.
 
-Finally, because the client does not want to dive deeply into the engine himself, he wants a secondary application (preferably a game) that can be used to test the engine's features. This is referred to as the `validation application.'
+So far, the client has made all his games in Unity and is very fond of the structure.
+Therefore, he wants the new engine to adhere to a similar structure. A simple
+requirements document is defined to specify how strictly this structure should be
+followed.
+
+Finally, because the client does not want to dive deeply into the engine himself, he
+wants a secondary application (preferably a game) that can be used to test the
+engine's features. This is referred to as the \emph{validation application}.
 
 \subsection{Goal}
-The goal is to develop a custom game engine that meets the client's requirements for maintainability, extensibility, user-friendliness, and adherence to a Unity-like structure. In addition, a validation application should be created to show and test the engine's features.
+
+The goal is to develop a custom game engine that meets the client's requirements for
+maintainability, extensibility, user-friendliness, and adherence to a Unity-like
+structure. In addition, a validation application should be created to show and test
+the engine's features.
 
 \subsection{Result}
-The expected result is a well-documented, custom game engine that follows a structure similar to Unity. Additionally, a validation application should be provided to test and showcase the engine's capabilities.
 
+The expected result is a well-documented, custom game engine that follows a structure
+similar to Unity. Additionally, a validation application should be provided to test
+and showcase the engine's capabilities.
 
 \section{Planning}
-The customer specified multiple deliverables troughout the 20 weeks of this project. Despite the fact that 20 weeks have been reserved for this project, the intention is that the game will be delivered in week 17. Any delay/resit will take place between weeks 17 and 20. A rough planning can be found in \cref{tab:planning}.
+
+The customer specified multiple deliverables troughout the 20 weeks of this project.
+Despite the fact that 20 weeks have been reserved for this project, the intention is
+that the game will be delivered in week 17. Any delay/resit will take place between
+weeks 17 and 20. A rough planning can be found in \cref{tab:planning}.
+
 \begin{table}
-	\begin{tabularx}{\linewidth}{lXr}
+	\begin{tabularx}{\linewidth}{lX}
 		\toprule
-		Week & Deliverable & \\
+		\textbf{Week\#} & \textbf{Deliverable}\\
 		\midrule
-		4 & Project Plan & \\
-		7 & Requirements document & \\
-		10 & POCs and Design document & \\
-		17 & Game engine, Validation application, Research document, and Qualification document & \\
+		4 & Project Plan\\
+		7 & Requirements document\\
+		10 & POCs and Design document\\
+		17 & Game engine, Validation application, Research document, and Qualification
+		document\\
 		\bottomrule
 	\end{tabularx}
 	\caption{Planning}
 	\label{tab:planning}
 \end{table}
 
-
 \section{Risks}
+
 \subsection{Techincal Risks}
-Multiplatform: The team works in both linux and windows, which poses a risk for the development if there is a platform dependency.
-Integration: Users can make a wrong integration causing for delay or risk of losing code.
+
+\begin{description}
+	\item[Multiplatform] The team works in both linux and windows, which poses a risk
+		for the development if there is a platform dependency.
+	\item[Integration] Users can make a wrong integration causing for delay or risk of
+		losing code.
+\end{description}
 
 \subsection{Project Management Risks}
-Scope Expansion: There is a risk of creating a scope that is bigger than the requirements.
-Lack of Team Collaboration: Insufficient collaboration among team members 
-may hinder progress.
+
+\begin{description}
+	\item[Scope Expansion] There is a risk of creating a scope that is bigger than the
+		requirements. Lack of Team Collaboration: Insufficient collaboration among team
+		members may hinder progress.
+\end{description}
 
 \subsection{Measures}
-Multiplatform: The team can switch any time to a single platform, so this risk is metigated. 
-Integration: By following standards and having an integrator, which checks every pull request, this risk is minimilised.
-Scope Expansion: By writing detailed requirements and having weekly team meetings, to check if the progress is within the scope, should be sufficient to decrease the risk.
-Lack of Team Collaboration: Weekly team meetings will result in collaboration among team members and discussing what each other tasks is, will decease this risk.
 
+\begin{description}
+	\item[Multiplatform] The team can switch any time to a single platform, so this
+		risk is metigated. 
+	\item[Integration] By following standards and having an integrator, which checks
+		every pull request, this risk is minimilised.
+	\item[Scope Expansion] By writing detailed requirements and having weekly team
+		meetings, to check if the progress is within the scope, should be sufficient to
+		decrease the risk.
+	\item[Lack of Team Collaboration] Weekly team meetings will result in collaboration
+		among team members and discussing what each other tasks is, will decease this
+		risk.
+\end{description}
 
-% Documentation Standard is described here
 \section{Documentation}
-This section describes the required documentation for the project, including the types of documents to be created and the standards they must adhere to.
+
+This section describes the required documentation for the project, including the
+types of documents to be created and the standards they must adhere to.
 
 \subsection{Documents}
+
 This project consists of seven main documents:\noparbreak
 \begin{description}
-	\item[Project Plan] Contains all elements related to the organization of the project, including timelines, milestones, roles, and responsibilities.
-	\item[Requirements] Details the requirements and user stories, including both functional and non-functional requirements.
+	\item[Project Plan] Contains all elements related to the organization of the
+		project, including timelines, milestones, roles, and responsibilities.
+	\item[Requirements] Details the requirements and user stories, including both
+		functional and non-functional requirements.
 	\item[Research] Consists of all research related to this project.
-	\item[Design] Describes the design choices, including architecture,
-	user interface, and system components.
-	\item[Qualification] Includes test cases, test plans, and quality 
-	measures to ensure the project meets its requirements and standards.
+	\item[Design] Describes the design choices, including architecture, user interface,
+		and system components.
+	\item[Qualification] Includes test cases, test plans, and quality measures to
+		ensure the project meets its requirements and standards.
 	\item[Working hours] A table which includes all working hours of each team member.
-	\item[API Reference] Details the available endpoints, request and response formats, authentication methods, error codes, and examples for interacting with the project's API.
+	\item[API Reference] Details the available endpoints, request and response formats,
+		authentication methods, error codes, and examples for interacting with the
+		project's API.
 \end{description}
 
 \subsection{Documentation Standard}
-The documentation standard can be found in the contributing.md \autocite{crepe:docs-standard}.
 
+The documentation standard can be found in the contributing.md
+\autocite{crepe:docs-standard}.
 
 \section{Work Agreements}
-Work agreements are the expectations and commitments made by the team members. 
-This section includes details on roles and responsibilities, documentation of 
-work hours, protocols for handling absences or delays, guidelines for addressing inconsistent participation, and procedures for weekly updates and meetings. All team members reviewed and agreed to these terms.
 
-\subsection{Project Roles}
-\begin{itemize}
-	\item \textbf{Loek Le Blansch}: Integrator
-	\item \textbf{Wouter Boerenkamps}: Project Member
-	\item \textbf{Jaro Rutjes}: Team Leader / Scrum Master
-	\item \textbf{Max Smits}: Project Member
-	\item \textbf{Niels Stunnebrink}: Project Member
-\end{itemize}
+Work agreements are the expectations and commitments made by the team members. This
+section includes details on roles and responsibilities, documentation of work hours,
+protocols for handling absences or delays, guidelines for addressing inconsistent
+participation, and procedures for weekly updates and meetings. All team members
+reviewed and agreed to these terms.
 
-\subsection{Work Hours}
-Each project member will keep track of their own working hours and add them to the `file'.
+\subsection{Project roles}
+
+\begin{description}
+	\item[Loek Le Blansch] Integrator
+	\item[Wouter Boerenkamps] Project Member
+	\item[Jaro Rutjes] Team Leader / Scrum Master
+	\item[Max Smits] Project Member
+	\item[Niels Stunnebrink] Project Member
+\end{description}
+
+\subsection{Work hours}
+
+Each project member will keep track of their own working hours and add them to the
+`file'.
+
+\subsection{Absence or delay}
 
-\subsection{Absence Or Delay}
 If a project member is going to be absent or delayed, they are required to 
 notify the team through either WhatsApp or Outlook. Additionally, the teacher 
 should be informed of the absence as well.
 
 \subsection{Inconsistent participation}
+
 Inconsistent participation will be addressed in a structured manner:
+
 \begin{description}
-	\item[Initial Discussion]: The team leader will first discuss the 
-	issue of inconsistent participation with the individual team member.
-	\item [Team Discussion]: If no improvement is observed, the issue 
-	will be brought up with the entire team to seek a collective solution.
-	\item [Project Supervisor Involvement]: Should the problem persist, despite these efforts, it will be escalated to the project supervisor (Bob van der Putten) for further action.
+	\item[Initial Discussion] The team leader will first discuss the issue of
+		inconsistent participation with the individual team member.
+	\item [Team Discussion] If no improvement is observed, the issue will be brought up
+		with the entire team to seek a collective solution.
+	\item [Project Supervisor Involvement] Should the problem persist, despite these
+		efforts, it will be escalated to the project supervisor (Bob van der Putten) for
+		further action.
 \end{description}
 
-Valid reasons for absence or inconsistency will be considered, and no 
-repercussions will be necessary in such cases. However, if the inconsistency is 
-due to other factors, potential repercussions may include additional assignments or actions as determined by the project supervisor.
+Valid reasons for absence or inconsistency will be considered, and no repercussions
+will be necessary in such cases. However, if the inconsistency is due to other
+factors, potential repercussions may include additional assignments or actions as
+determined by the project supervisor.
 
 A team member is considered to have inconsistent participation if their hours 
 are significantly behind the team’s average or if tasks are not completed 
@@ -138,8 +206,11 @@ without valid reasons. It is essential that any concerns regarding a team
 member's performance, should be resolved through unanimous agreement within the team.
 
 \subsection{Weekly Update}
-Each team member is required to send their technical weekly update to Jaro 
-Rutjes before 12:00 on Friday. Personal updates may also be included and will be confidential between the team member, the Team Leader, and/or the teacher, if requested.
+
+Each team member is required to send their technical weekly update to Jaro Rutjes
+before 12:00 on Friday. Personal updates may also be included and will be
+confidential between the team member, the Team Leader, and/or the teacher, if
+requested.
 
 Jaro Rutjes will compile and send a team update to the project supervisor, Bob 
 van der Putten, also by Friday. This update will include technical information 
@@ -147,63 +218,82 @@ on what has been accomplished, plans for upcoming work, and an overview of the
 team's overall progress.
 
 \subsection{Weekly Meetings}
-The project team will hold at least two meetings each week, with each meeting 
-lasting a maximum of 30 minutes. Following these meetings, an additional one 
-hour will be scheduled for discussions on the topics covered.
 
-Meetings will be planned and discussed one week in advance, with invitations 
-sent via Outlook. Additional meetings may be scheduled if necessary to address 
-issues or project needs.
+The project team will hold at least two meetings each week, with each meeting lasting
+a maximum of 30 minutes. Following these meetings, an additional one hour will be
+scheduled for discussions on the topics covered.
 
+Meetings will be planned and discussed one week in advance, with invitations sent via
+Outlook. Additional meetings may be scheduled if necessary to address issues or
+project needs.
 
 % Information about how and when Scrum will be used in this project (using Miro).
 \section{Scrum (Miro)}
-The team will start using scrum after the initial phase, of structering the project, is finished. This phase is over when proof of concepts are being made.
+
+The team will start using scrum after the initial phase, of structering the project,
+is finished. This phase is over when proof of concepts are being made.
 
 \subsection{Scrum Board}
+
 The Scrum board \autocite{miro:scrum-board} will consist of the following tabs:
+
 \begin{description}
-	\item[Backlog]: This tab contains a list of all tasks and user stories that are planned for future sprints.
-	\item[Next Sprint]: This tab includes tasks and user stories that have been selected for the upcoming sprint.
-	\item[Current Sprint]: This tab displays the tasks and user stories that are actively being worked on in the current sprint.
-	\item[In Progress]: Tasks that are actively being worked on are moved to this tab.
-	\item[Review]: Completed tasks that are awaiting review or testing will be placed in this tab.
-	\item[Done]: Once tasks have been reviewed and are considered complete, they are moved to the Done tab.
-	\item [Blocked]: This tab is used for tasks that cannot proceed due to obstacles or dependencies.
+	\item[Backlog] This tab contains a list of all tasks and user stories that are
+		planned for future sprints.
+	\item[Next Sprint] This tab includes tasks and user stories that have been selected
+		for the upcoming sprint.
+	\item[Current Sprint] This tab displays the tasks and user stories that are
+		actively being worked on in the current sprint.
+	\item[In Progress] Tasks that are actively being worked on are moved to this tab.
+	\item[Review] Completed tasks that are awaiting review or testing will be placed in
+		this tab.
+	\item[Done] Once tasks have been reviewed and are considered complete, they are
+		moved to the Done tab.
+	\item [Blocked] This tab is used for tasks that cannot proceed due to obstacles or
+		dependencies.
 \end{description}
 
-\noindent
 To manage tasks effectively:
+
 \begin{itemize}
-	\item A task from the \emph{Current Sprint} tab should be selected and moved to the \emph{In Progress} tab when work begins. 
-	\item The status of the task should be updated to \emph{In Progress} as soon as work starts.
-	\item Once the task is completed and reviewed, it should be moved to the \emph{Done} tab, and its status should be updated to \emph{Done}.
+	\item A task from the \emph{Current Sprint} tab should be selected and moved to the
+		\emph{In Progress} tab when work begins. 
+	\item The status of the task should be updated to \emph{In Progress} as soon as
+		work starts.
+	\item Once the task is completed and reviewed, it should be moved to the
+		\emph{Done} tab, and its status should be updated to \emph{Done}.
 \end{itemize}
 
-\noindent
-Each task or user story will be assigned user points, which indicate the relative size or complexity of the task compared to these examples.
+Each task or user story will be assigned user points, which indicate the relative
+size or complexity of the task compared to these examples.
 % TODO: add examples
 
 \subsection{Burn Down Chart}
-The Burn Down Chart will be generated using Excel from the Scrum board data every week.
-Each user story or task marked as done, will burn the chart downwards.
-This Burn Down Chart is shared in the weekly updates with the team and the project supervisor.
 
+The Burn Down Chart will be generated using Excel from the Scrum board data every
+week. Each user story or task marked as done, will burn the chart downwards. This
+Burn Down Chart is shared in the weekly updates with the team and the project
+supervisor.
 
-% Information regarding the git workflow of this project. Diagram explaining the workflow
+% Information regarding the git workflow of this project.
+% TODO: Diagram explaining the workflow
 \section{Git Workflow}
-GitHub is used for version management of both code and documentation, each in its own respective repository.
-This keeps the documentation and code seperate, resulting in ordened and manageable repositories.
-\begin{itemize}
-	\item Code Repository: crepe \autocite{crepe:code-repo}
-	\item Documentation Repository: crepe-docs \autocite{crepe:docs-repo}
-\end{itemize}
+
+GitHub is used for version management of both code \autocite{crepe:code-repo} and
+documentation \autocite{crepe:docs-repo}, each in its own respective repository. This
+keeps the documentation and code seperate, resulting in ordened and manageable
+repositories.
 
 \subsection{Git New Branch}
+
 % TODO: add here details from contributing.md?
+
 \subsection{Git Merge To Master}
+
 % TODO: add here diagram on what actions are taken before merge.
+
 \subsection{Code Standard}
+
 The code standard can be found in the contributing.md \autocite{crepe:code-standard}.
 
 \end{document}
diff --git a/projdoc.cls b/projdoc.cls
index cc25e67..c0257ca 100644
--- a/projdoc.cls
+++ b/projdoc.cls
@@ -119,6 +119,16 @@
 	itemsep=\dimexpr\style@itemsep-\style@parsep\relax,
 	parsep=\style@parsep,
 }
+\def\projdoc@setdescriptionstyle{%
+	\renewcommand\makelabel[1]{%
+		{\bfseries ##1}:%
+	}%
+}
+\setdescription{
+	before={\projdoc@setdescriptionstyle},
+	leftmargin=3em,
+	labelindent=3ex,
+}
 \makeatother
 
 % create a label using \customlabel[<creftype>]{<refname>}{<reftext>} that displays
@@ -213,10 +223,16 @@
 \def\projdoc@trailer{
 	% bibliography (if citations used)
 	\ifbool{projdoc@used@cite}{%
+		\hfuzz=50pt% reduce overfull hbox warnings for bibliography (mostly URLs)
 		\printbibliography%
 	}{}%
 	% glossary
-	\ifbool{projdoc@used@cite}{%
+	\ifbool{projdoc@used@gls}{%
+		\setdescription{
+			before={\projdoc@setdescriptionstyle},
+			leftmargin=2ex,
+			labelindent=0pt,
+		}%
 		\section*{Glossary}%
 		\begin{multicols}{2}%
 			\renewcommand{\glossarysection}[2][]{}%
diff --git a/sources.bib b/sources.bib
index 5848f86..50f5ead 100644
--- a/sources.bib
+++ b/sources.bib
@@ -13,35 +13,35 @@
 }
 
 @misc{miro:scrum-board,
-  author = {Jaro Rutjes},
+  author = {Loek Le Blansch and Wouter Boerenkamps and Jaro Rutjes and Max Smits and Niels Stunnebrink},
   title = {Scrum Board on Miro},
   url = {https://miro.com/app/board/uXjVKjtdM64=/?share_link_id=303851465474},
   date = {2024-09-10},
 }
 
 @misc{crepe:code-repo,
-  author = {lonkaars},
+  author = {Loek Le Blansch and Wouter Boerenkamps and Jaro Rutjes and Max Smits and Niels Stunnebrink},
   title = {Crepe Code Repository},
   url = {https://github.com/lonkaars/crepe},
   date = {2024-09-10},
 }
 
 @misc{crepe:docs-repo,
-  author = {lonkaars},
+  author = {Loek Le Blansch and Wouter Boerenkamps and Jaro Rutjes and Max Smits and Niels Stunnebrink},
   title = {Crepe Documentation Repository},
   url = {https://github.com/lonkaars},
   date = {2024-09-10},
 }
 
 @misc{crepe:docs-standard,
-  author = {lonkaars},
+  author = {Loek Le Blansch and Wouter Boerenkamps and Jaro Rutjes and Max Smits and Niels Stunnebrink},
   title = {Crepe Documentation Standard},
   url = {https://github.com/lonkaars/crepe-docs/blob/master/contributing.md},
   date = {2024-09-10},
 }
 
 @misc{crepe:code-standard,
-  author = {lonkaars},
+  author = {Loek Le Blansch and Wouter Boerenkamps and Jaro Rutjes and Max Smits and Niels Stunnebrink},
   title = {Crepe Code Standard},
   url = {https://github.com/lonkaars/crepe/blob/master/contributing.md},
   date = {2024-09-10},
-- 
cgit v1.2.3


From 4d085ec22a6d8560913f61096a83ebda4bbaa740 Mon Sep 17 00:00:00 2001
From: Loek Le Blansch <loek@pipeframe.xyz>
Date: Sat, 14 Sep 2024 11:10:00 +0200
Subject: clean up LaTeX build system

---
 .gitignore       |  2 --
 example.tex      | 10 ++++++----
 img/.gitignore   |  2 ++
 img/example.puml |  6 ++++++
 latexmkrc        | 61 ++++++++++++++++++++------------------------------------
 makefile         | 17 ----------------
 projdoc.cls      |  4 +++-
 7 files changed, 39 insertions(+), 63 deletions(-)
 create mode 100644 img/.gitignore
 create mode 100644 img/example.puml
 delete mode 100644 makefile

(limited to 'projdoc.cls')

diff --git a/.gitignore b/.gitignore
index efae07a..dfaa9e7 100644
--- a/.gitignore
+++ b/.gitignore
@@ -29,8 +29,6 @@
 
 # output files
 *.pdf
-!img/*.pdf
-img/*.puml.pdf
 
 # generated files
 time.tex
diff --git a/example.tex b/example.tex
index ce45f6f..4ae4d95 100644
--- a/example.tex
+++ b/example.tex
@@ -35,7 +35,7 @@ These don't show up in the table of contents by default
 
 \Cref{fig:example-a} definitely shows something. Referencing multiple things at once
 is neatly handled by cleveref:
-\cref{fig:example-a,fig:example-b,fig:example-c,tab:gate-xor}.
+\cref{fig:example-a,fig:example-b,fig:example-puml,tab:gate-xor}.
 
 \begin{figure}
 	\centering
@@ -46,15 +46,17 @@ is neatly handled by cleveref:
 
 \begin{figure}
 	\begin{subfigure}{0.45\textwidth}%
+		\centering
 		\fitimg{\includegraphics{example-image-b}}
 		\caption{Example image B}
 		\label{fig:example-b}
 	\end{subfigure}%
 	\hfill
 	\begin{subfigure}{0.45\textwidth}%
-		\fitimg{\includegraphics{example-image-c}}
-		\caption{Example image C}
-		\label{fig:example-c}
+		\centering
+		\includepumldiag{img/example.puml}
+		\caption{Example PlantUML diagram}
+		\label{fig:example-puml}
 	\end{subfigure}%
 	\caption{Subfigures}
 \end{figure}
diff --git a/img/.gitignore b/img/.gitignore
new file mode 100644
index 0000000..60a51f5
--- /dev/null
+++ b/img/.gitignore
@@ -0,0 +1,2 @@
+!*.pdf
+*.eps
diff --git a/img/example.puml b/img/example.puml
new file mode 100644
index 0000000..942dce2
--- /dev/null
+++ b/img/example.puml
@@ -0,0 +1,6 @@
+@startuml
+!include theme.ipuml
+
+Bob -> Alice : hello
+
+@enduml
diff --git a/latexmkrc b/latexmkrc
index 66ed0d1..293cf09 100644
--- a/latexmkrc
+++ b/latexmkrc
@@ -1,49 +1,32 @@
-$pdflatex = "xelatex %O %S";
+# https://nl.mirrors.cicku.me/ctan/support/latexmk/latexmk.pdf
+
+$pdflatex = "xelatex --interaction=nonstopmode %O %S";
 $pdf_mode = 1;
 $dvi_mode = 0;
 $postscript_mode = 0;
+$clean_ext .= ' %R.ist %R.xdy bbl run.xml';
+@default_files = (
+	'example',
+	'plan',
+	'research',
+	'timerep',
+);
 
-# https://tex.stackexchange.com/questions/400325/latexmkrc-for-bib2gls
-add_cus_dep('glo', 'gls', 0, 'run_makeglossaries');
-add_cus_dep('acn', 'acr', 0, 'run_makeglossaries');
-add_cus_dep('aux', 'glstex', 0, 'run_bib2gls');
+push @file_not_found, '^Package .* No file `([^\\\']*)\\\'';
+push @generated_exts, 'glo', 'gls', 'glg';
 
-sub run_makeglossaries {
-  if ( $silent ) {
-    system "makeglossaries -q '$_[0]'";
-  } else {
-    system "makeglossaries '$_[0]'";
-  };
+add_cus_dep('aux', 'glstex', 0, 'bib2gls');
+sub bib2gls {
+	return system "bib2gls '$_[0]'";
 }
 
-sub run_bib2gls {
-  if ( $silent ) {
-    my $ret = system "bib2gls --silent --group '$_[0]'";
-  } else {
-    my $ret = system "bib2gls --group '$_[0]'";
-  };
-  my ($base, $path) = fileparse( $_[0] );
-  if ($path && -e "$base.glstex") {
-    rename "$base.glstex", "$path$base.glstex";
-  }
-  # Analyze log file.
-  local *LOG;
-  $LOG = "$_[0].glg";
-  if (!$ret && -e $LOG) {
-    open LOG, "<$LOG";
-    while (<LOG>) {
-      if (/^Reading (.*\.bib)\s$/) {
-        rdb_ensure_file( $rule, $1 );
-      }
-    }
-    close LOG;
-  }
-  return $ret;
+add_cus_dep('puml', 'eps', 0, 'plantuml');
+sub plantuml {
+  return system "plantuml -teps '$_[0].puml'";
 }
 
-push @file_not_found, '^Package .* No file `([^\\\']*)\\\'';
-push @generated_exts, 'glo', 'gls', 'glg';
-push @generated_exts, 'acn', 'acr', 'alg';
-$clean_ext .= ' %R.ist %R.xdy';
-$clean_ext .= ' bbl run.xml';
+add_cus_dep('txt', 'tex', 0, 'time2tex');
+sub time2tex {
+	return system "./time2tex.py '$_[0].txt' > '$_[0].tex'";
+}
 
diff --git a/makefile b/makefile
deleted file mode 100644
index 15eae95..0000000
--- a/makefile
+++ /dev/null
@@ -1,17 +0,0 @@
-all: plan.pdf
-all: research.pdf
-
-LATEXMKFLAGS += -cd
-LATEXMKFLAGS += -interaction=nonstopmode
-%.pdf: %.tex
-	-latexmk $(LATEXMKFLAGS) $<
-
-%.puml.pdf: %.puml
-	plantuml -tpdf $<
-	mv $*.pdf $@
-
-%.tex: %.txt
-	./time2tex.py $< > $@
-
-timerep.pdf: time.tex
-
diff --git a/projdoc.cls b/projdoc.cls
index c0257ca..0d27a1f 100644
--- a/projdoc.cls
+++ b/projdoc.cls
@@ -58,6 +58,7 @@
 \RequirePackage{subcaption}
 \RequirePackage{multicol}
 \RequirePackage{comparison} % ./comparison.sty
+\RequirePackage{xstring}
 
 % font style
 \setmainfont{TeX Gyre Schola}
@@ -297,7 +298,8 @@
 
 % adjust scale for puml diagrams
 \newcommand{\includepumldiag}[1]{%
-	\fitimg{\includegraphics[scale=0.6]{#1}}%
+	\StrSubstitute{#1}{.puml}{.eps}[\filename]%
+	\fitimg{\includegraphics[scale=0.6]{\filename}}%
 }
 
 % prevent page break between two paragraphs
-- 
cgit v1.2.3


From f6cb1e9d141d881ae6205027626d6643776e833c Mon Sep 17 00:00:00 2001
From: Loek Le Blansch <loek@pipeframe.xyz>
Date: Sun, 15 Sep 2024 20:46:48 +0200
Subject: WIP requirements

---
 example.tex         |  13 +++-
 latexmkrc           |   5 +-
 projdoc.cls         |   8 ++
 reqs2tex.py         |  27 -------
 scripts/.gitignore  |   1 +
 scripts/reqs2tex.py |  83 ++++++++++++++++++++
 scripts/tex.py      |  43 +++++++++++
 scripts/time2tex.py | 213 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 time2tex.py         | 204 -------------------------------------------------
 9 files changed, 362 insertions(+), 235 deletions(-)
 delete mode 100755 reqs2tex.py
 create mode 100644 scripts/.gitignore
 create mode 100755 scripts/reqs2tex.py
 create mode 100644 scripts/tex.py
 create mode 100755 scripts/time2tex.py
 delete mode 100755 time2tex.py

(limited to 'projdoc.cls')

diff --git a/example.tex b/example.tex
index 4ae4d95..ee1377a 100644
--- a/example.tex
+++ b/example.tex
@@ -3,6 +3,8 @@
 % with the [draft] option. this replaces all images with placeholders.
 \input{meta.tex}
 
+\input{reqs.aux}
+
 \title{Example Document}
 
 \begin{document}
@@ -155,17 +157,24 @@ Description:
 	\con{Bad thing 2}
 \end{comparison}
 
-\subsection{Citations}
+\subsection{References}
+
+\subsubsection{Citations}
 
 Citations are inserted using the \codeinline{\autocite} command \autocite{rfc:3339}.
 The bibliography is automatically printed after \codeinline{\end{document}}.
 
-\subsection{Glossary}
+\subsubsection{Glossary}
 
 Glossary entries can be inserted using the \codeinline{\gls} commands. Example:
 ``\Gls{sdl2} handles \glspl{hid} as well!''. In following occurrences of acronyms,
 only their short form is printed: `\gls{sdl2}' and `\gls{hid}'. All of these link to
 the glossary that is automatically printed after \codeinline{\end{document}}.
 
+\subsubsection{Requirements}
+
+Requirements are referenced like \codeinline{\label}s:
+e.g.~\cref{req:audio:handle:id,req:audio:async-api:id}.
+
 \end{document}
 
diff --git a/latexmkrc b/latexmkrc
index 2ce20fa..5a49064 100644
--- a/latexmkrc
+++ b/latexmkrc
@@ -26,11 +26,12 @@ sub plantuml {
 
 add_cus_dep('txt', 'tex', 0, 'time2tex');
 sub time2tex {
-	return system "python3 time2tex.py '$_[0].txt'";
+	return system "python3 scripts/time2tex.py '$_[0].txt'";
 }
 
 add_cus_dep('toml', 'tex', 0, 'reqs2tex');
+add_cus_dep('toml', 'aux', 0, 'reqs2tex');
 sub reqs2tex {
-	return system "python3 reqs2tex.py '$_[0].toml'";
+	return system "python3 scripts/reqs2tex.py '$_[0].toml'";
 }
 
diff --git a/projdoc.cls b/projdoc.cls
index 0d27a1f..c11fe61 100644
--- a/projdoc.cls
+++ b/projdoc.cls
@@ -307,3 +307,11 @@
 \newcommand\noparbreak{\par\nobreak\@afterheading}
 \makeatother
 
+% cleveref extra types
+\crefname{paragraph}{paragraph}{paragraphs}
+\Crefname{paragraph}{Paragraph}{Paragraphs}
+\crefname{requirement}{requirement}{requirements}
+\Crefname{requirement}{Requirement}{Requirements}
+\crefname{test}{test}{tests}
+\Crefname{test}{Test}{Tests}
+
diff --git a/reqs2tex.py b/reqs2tex.py
deleted file mode 100755
index 68c8f40..0000000
--- a/reqs2tex.py
+++ /dev/null
@@ -1,27 +0,0 @@
-#!/bin/python3
-import sys, tomllib
-
-def fmt(data):
-  print(data)
-  return f"""
-\\makeatletter
-\\makeatother
-"""
-
-def main(input_file):
-  data = {}
-  with open(input_file, "rb") as file:
-    data = tomllib.load(file)
-
-  output = fmt(data)
-
-  output_file = input_file.removesuffix(".toml") + ".tex"
-  with open(output_file, "w+") as file:
-    file.write(output)
-
-if __name__ == "__main__":
-  if len(sys.argv) != 2:
-    print("usage: reqs2tex.py reqs.toml")
-    exit(1)
-  main(sys.argv[1])
-
diff --git a/scripts/.gitignore b/scripts/.gitignore
new file mode 100644
index 0000000..bee8a64
--- /dev/null
+++ b/scripts/.gitignore
@@ -0,0 +1 @@
+__pycache__
diff --git a/scripts/reqs2tex.py b/scripts/reqs2tex.py
new file mode 100755
index 0000000..9e71a48
--- /dev/null
+++ b/scripts/reqs2tex.py
@@ -0,0 +1,83 @@
+#!/bin/python3
+import sys, tomllib, tex
+
+def flatten(data):
+  if 'description' in data:
+    return [ data ]
+  out = []
+  for key, value in data.items():
+    items = flatten(value)
+    for item in items:
+      if 'label' in item:
+        item['label'] = f"{key}:{item['label']}"
+      else:
+        item['label'] = f"{key}"
+    out += items
+  return out
+
+id_counter = 0
+def make_id(item):
+  global id_counter
+  id_counter += 1
+  return "{type_short}#{counter:03d}".format(
+    type_short = item['type'][0].upper(),
+    counter = id_counter,
+  )
+
+def convert(data):
+  reqs = flatten(data)
+  for index, item in enumerate(reqs):
+    item['id'] = tex.esc(make_id(item))
+    item['index'] = index
+    item['description'] = item.get('description', '???')
+    item['done'] = item.get('done', None)
+    item['priority'] = item.get('priority', 'must')
+    item['type'] = item.get('type', 'system')
+    item['deleted'] = item.get('deleted', False)
+
+  # skip deleted requirements (but process for make_id)
+  reqs = [item for item in reqs if item['deleted'] == False]
+
+  return reqs
+
+def req2aux(req):
+  # TODO: this is a dead-end solution, newlabel only works for in-document anchors, not external links
+  out = [
+    tex.scmd('newlabel', f"req:{req['label']}:id", tex.group(req['id'], req['id'], '', './requirements.pdf', '')),
+    tex.scmd('newlabel', f"req:{req['label']}:id@cref", tex.group(f"[requirement][][]{req['id']}", '')),
+  ]
+  return "\n".join([tex.auxout(line) for line in out])
+
+def fmt_aux(data):
+  out = ""
+  out += tex.cmd('makeatletter')
+  out += "\n".join([req2aux(req) for req in data])
+  out += tex.cmd('makeatother')
+  return out
+
+def fmt_tex(data):
+  return "\n".join([
+    tex.cmd('relax')
+  ])
+
+def main(input_file):
+  data = {}
+  with open(input_file, "rb") as file:
+    data = tomllib.load(file)
+
+  requirements = convert(data)
+
+  output_aux = input_file.removesuffix(".toml") + ".aux"
+  with open(output_aux, "w+") as file:
+    file.write(fmt_aux(requirements))
+
+  output_tex = input_file.removesuffix(".toml") + ".tex"
+  with open(output_tex, "w+") as file:
+    file.write(fmt_tex(requirements))
+
+if __name__ == "__main__":
+  if len(sys.argv) != 2:
+    print("usage: reqs2tex.py reqs.toml")
+    exit(1)
+  main(sys.argv[1])
+
diff --git a/scripts/tex.py b/scripts/tex.py
new file mode 100644
index 0000000..b044857
--- /dev/null
+++ b/scripts/tex.py
@@ -0,0 +1,43 @@
+# utility function for converting latex code
+
+def group(*args):
+  return "".join("{" + arg + "}" for arg in args)
+
+def string(content):
+  return r"\string" + content
+
+def cmd(*args):
+  name = args[0]
+  args = args[1:]
+  if len(args) == 0: args = [""]
+  return f"\\{name}" + group(*args)
+
+def csdef(*args):
+  return r"\def" + cmd(*args)
+
+def auxout(content):
+  return r"\write\@auxout" + group(content)
+
+def scmd(*args):
+  return string(cmd(*args))
+
+def env(name, *args):
+  content = args[-1]
+  args = args[0:-1]
+  out = f"\\begin{{{name}}}"
+  if len(args) > 0:
+    out += group(*args)
+  out += content
+  out += f"\\end{{{name}}}"
+  return out
+
+def esc(plain):
+  plain = plain.replace("\\", "\\string\\")
+  plain = plain.replace("#", "\\#")
+  plain = plain.replace("$", "\\$")
+  plain = plain.replace("%", "\\%")
+  return plain
+
+def tabrule(*cells):
+  return "&".join(cells) + "\\\\"
+
diff --git a/scripts/time2tex.py b/scripts/time2tex.py
new file mode 100755
index 0000000..17661d5
--- /dev/null
+++ b/scripts/time2tex.py
@@ -0,0 +1,213 @@
+#!/bin/python3
+import sys, tex
+from datetime import datetime, timedelta
+
+def fmt_duration(sec):
+  mins = (sec + 59) // 60 # integer divide, round up
+  out = []
+
+  if mins == 0:
+    return "--"
+
+  hour = mins // 60
+  if hour > 0:
+    out.append("%02dh" % (hour, ))
+    mins = mins % 60
+
+  out.append("%02dm" % (mins, ))
+
+  return "\\,".join(out)
+
+def fmt_percentage(fac):
+  return tex.sgroup(
+    tex.cmd('footnotesize') +\
+    tex.cmd('itshape') +\
+    tex.esc(f"({round(fac * 100)}%)")
+  )
+
+def fmt_member_overview(times):
+  # calculations
+  tracked = {}
+  total_time = 0
+  for time in times:
+    if not time["name"] in tracked:
+      tracked[time["name"]] = 0
+    tracked[time["name"]] += time["duration"]
+    total_time += time["duration"]
+
+  out = ""
+
+  # header
+  out += tex.cmd('toprule')
+  out += tex.tabrule(tex.cmd('textbf', 'Member'), tex.cmd('textbf', 'Tracked'), '')
+  out += tex.cmd('midrule')
+
+  # member overview
+  members = sorted(list(set(time["name"] for time in times)))
+  for name in members:
+    out += tex.tabrule(name, fmt_duration(tracked[name]), fmt_percentage(tracked[name] / total_time))
+  out += tex.cmd('midrule')
+
+  # sum
+  out += tex.tabrule('', fmt_duration(total_time), '')
+  out += tex.cmd('bottomrule')
+
+  out = tex.env('tabular', 'lr@{~}l', out)
+  out = tex.cmd('centering') +\
+        out +\
+        tex.cmd('caption', 'Tracked time per group member') +\
+        tex.cmd('label', 'tab:time-member')
+  out = tex.env('table', out)
+
+  return out
+
+def fmt_weekly_overview(times):
+  # calculations
+  out = ""
+  weeks = []
+  member_totals = {}
+  total_time = sum(time["duration"] for time in times)
+  members = sorted(list(set(time["name"] for time in times)))
+  time_start = min(time["date"] for time in times)
+  time_end = max(time["date"] for time in times)
+  week_start = time_start - timedelta(days=time_start.weekday()) # round down to nearest monday
+  week_end = time_end + timedelta(days=7-time_end.weekday())
+
+  week = week_start
+  week_num = 1
+  while week < week_end:
+    week_times = [time for time in times if time["date"] >= week and time["date"] < (week + timedelta(days=7))]
+
+    week_entry = {
+      "num": week_num,
+      "members": {},
+      "total": sum(time["duration"] for time in week_times)
+    }
+
+    for member in members:
+      week_entry["members"][member] = sum(time["duration"] for time in week_times if time["name"] == member)
+
+    weeks.append(week_entry)
+    week_num += 1
+    week += timedelta(days=7)
+  for member in members:
+    member_totals[member] = sum(time["duration"] for time in times if time["name"] == member)
+
+  # TODO: refactor
+  # begin table
+  out += r"\begin{table}\centering"
+  out += r"\fitimg{"
+  out += f"\\begin{{tabular}}{{l{'r@{~}l' * len(members)}@{{\\qquad}}r}}\\toprule"
+  out += r"\textbf{\#}"
+  for member in members:
+    out += f"&\\textbf{{{member}}}&"
+  out += r"&\textbf{Subtotal}\\\midrule{}"
+
+  for entry in weeks:
+    out += f"{entry['num']}"
+    for member in members:
+      out += f"&{fmt_duration(entry['members'][member])}&{fmt_percentage(entry['members'][member] / entry['total'])}"
+    out += f"&{fmt_duration(entry['total'])}\\\\"
+
+  out += r"\midrule{}"
+  for member in members:
+    out += f"&{fmt_duration(member_totals[member])}&{fmt_percentage(member_totals[member] / total_time)}"
+  out += f"&{fmt_duration(total_time)}\\\\"
+
+  # end table
+  out += r"\bottomrule\end{tabular}"
+  out += r"}" # \fitimg
+  out += r"\caption{Tracked time per week}\label{tab:time-weekly}"
+  out += r"\end{table}"
+
+  return out
+
+def duration2secs(duration):
+  out = 0 # output (seconds)
+  cur = 0 # current figure (unknown)
+  for c in duration:
+    if c.isdigit():
+      cur = cur * 10 + int(c)
+      continue
+    if c == "h":
+      out += cur * 3600
+      cur = 0
+      continue
+    if c == "m":
+      out += cur * 60
+      cur = 0
+      continue
+    if c == "s":
+      out += cur * 1
+      cur = 0
+      continue
+
+    raise Exception("invalid duration format")
+  if cur != 0: raise Exception("invalid duration format")
+  return out
+
+def line2data(line):
+  # parse fields from input string
+  data = {}
+  next = line.find(':')
+  data["name"] = line[0:next].strip()
+  line = line[next+1:].strip()
+  next = line.find(' ')
+  data["date"] = line[0:next].strip()
+  line = line[next+1:].strip()
+  next = line.find(' ')
+  data["duration"] = line[0:next].strip()
+  line = line[next+1:].strip()
+  data["description"] = line
+
+  # deserialize parsed fields
+  data["name"] = data["name"].title()
+  data["date"] = datetime.strptime(data["date"], '%Y-%m-%d')
+  data["duration"] = duration2secs(data["duration"])
+  data["description"] = [el.strip() for el in data["description"].split("::")]
+
+  return data
+
+def parse(content):
+  # split content at newlines
+  lines = content.split("\n")
+  out = []
+  for i, line in enumerate(lines):
+    line = line.strip()
+    if line.startswith("#"): continue
+    if len(line) == 0: continue
+
+    try: out.append(line2data(line))
+    except Exception as e: raise Exception(f"line {i+1}: {e}")
+  return out
+
+def fmt(times):
+  return "\n\n".join([
+    tex.cmd('section', 'Overviews'),
+    tex.cmd('subsection', 'Members'),
+    fmt_member_overview(times),
+    tex.cmd('subsection', 'Weekly'),
+    fmt_weekly_overview(times),
+  ])
+
+def main(input_file):
+  content = ""
+  with open(input_file, "r") as file:
+    content = file.read()
+
+  try: parsed = parse(content)
+  except Exception as e:
+    print(f"{input_file}: {e}")
+    exit(1)
+  output = fmt(parsed)
+
+  output_file = input_file.removesuffix(".txt") + ".tex"
+  with open(output_file, "w+") as file:
+    file.write(output)
+
+if __name__ == "__main__":
+  if len(sys.argv) != 2:
+    print("usage: time2tex.py time.txt")
+    exit(1)
+  main(sys.argv[1])
+
diff --git a/time2tex.py b/time2tex.py
deleted file mode 100755
index 6e3de9c..0000000
--- a/time2tex.py
+++ /dev/null
@@ -1,204 +0,0 @@
-#!/bin/python3
-import sys
-from datetime import datetime, timedelta
-
-def fmt_duration(sec):
-  mins = (sec + 59) // 60 # integer divide, round up
-  out = []
-
-  if mins == 0:
-    return "--"
-
-  hour = mins // 60
-  if hour > 0:
-    out.append("%02dh" % (hour, ))
-    mins = mins % 60
-
-  out.append("%02dm" % (mins, ))
-
-  return "\\,".join(out)
-
-def fmt_percentage(fac):
-  return f"{{\\footnotesize\\itshape({round(fac * 100)}\\%)}}"
-
-def fmt_member_overview(times):
-  # calculations
-  out = ""
-  tracked = {}
-  total_time = 0
-  for time in times:
-    if not time["name"] in tracked:
-      tracked[time["name"]] = 0
-    tracked[time["name"]] += time["duration"]
-    total_time += time["duration"]
-
-  # begin table
-  out += r"\begin{table}\centering"
-  out += r"\begin{tabular}{lr@{~}l}\toprule"
-  out += r"\textbf{Member} & \textbf{Tracked} &\\\midrule{}"
-
-  # member overview
-  members = sorted(list(set(time["name"] for time in times)))
-  for name in members:
-    out += f"{name} & {fmt_duration(tracked[name])} & {fmt_percentage(tracked[name] / total_time)}\\\\"
-  out += r"\midrule{}"
-
-  # sum
-  out += f"&{fmt_duration(total_time)}&\\\\"
-
-  # end table
-  out += r"\bottomrule\end{tabular}"
-  out += r"\caption{Tracked time per group member}\label{tab:time-member}"
-  out += r"\end{table}"
-
-  return out
-
-def fmt_weekly_overview(times):
-  # calculations
-  out = ""
-  weeks = []
-  member_totals = {}
-  total_time = sum(time["duration"] for time in times)
-  members = sorted(list(set(time["name"] for time in times)))
-  time_start = min(time["date"] for time in times)
-  time_end = max(time["date"] for time in times)
-  week_start = time_start - timedelta(days=time_start.weekday()) # round down to nearest monday
-  week_end = time_end + timedelta(days=7-time_end.weekday())
-
-  week = week_start
-  week_num = 1
-  while week < week_end:
-    week_times = [time for time in times if time["date"] >= week and time["date"] < (week + timedelta(days=7))]
-
-    week_entry = {
-      "num": week_num,
-      "members": {},
-      "total": sum(time["duration"] for time in week_times)
-    }
-
-    for member in members:
-      week_entry["members"][member] = sum(time["duration"] for time in week_times if time["name"] == member)
-
-    weeks.append(week_entry)
-    week_num += 1
-    week += timedelta(days=7)
-  for member in members:
-    member_totals[member] = sum(time["duration"] for time in times if time["name"] == member)
-
-  # begin table
-  out += r"\begin{table}\centering"
-  out += r"\fitimg{"
-  out += f"\\begin{{tabular}}{{l{'r@{~}l' * len(members)}@{{\\qquad}}r}}\\toprule"
-  out += r"\textbf{\#}"
-  for member in members:
-    out += f"&\\textbf{{{member}}}&"
-  out += r"&\textbf{Subtotal}\\\midrule{}"
-
-  for entry in weeks:
-    out += f"{entry['num']}"
-    for member in members:
-      out += f"&{fmt_duration(entry['members'][member])}&{fmt_percentage(entry['members'][member] / entry['total'])}"
-    out += f"&{fmt_duration(entry['total'])}\\\\"
-
-  out += r"\midrule{}"
-  for member in members:
-    out += f"&{fmt_duration(member_totals[member])}&{fmt_percentage(member_totals[member] / total_time)}"
-  out += f"&{fmt_duration(total_time)}\\\\"
-
-  # end table
-  out += r"\bottomrule\end{tabular}"
-  out += r"}" # \fitimg
-  out += r"\caption{Tracked time per week}\label{tab:time-weekly}"
-  out += r"\end{table}"
-
-  return out
-
-def duration2secs(duration):
-  out = 0 # output (seconds)
-  cur = 0 # current figure (unknown)
-  for c in duration:
-    if c.isdigit():
-      cur = cur * 10 + int(c)
-      continue
-    if c == "h":
-      out += cur * 3600
-      cur = 0
-      continue
-    if c == "m":
-      out += cur * 60
-      cur = 0
-      continue
-    if c == "s":
-      out += cur * 1
-      cur = 0
-      continue
-
-    raise Exception("invalid duration format")
-  if cur != 0: raise Exception("invalid duration format")
-  return out
-
-def line2data(line):
-  # parse fields from input string
-  data = {}
-  next = line.find(':')
-  data["name"] = line[0:next].strip()
-  line = line[next+1:].strip()
-  next = line.find(' ')
-  data["date"] = line[0:next].strip()
-  line = line[next+1:].strip()
-  next = line.find(' ')
-  data["duration"] = line[0:next].strip()
-  line = line[next+1:].strip()
-  data["description"] = line
-
-  # deserialize parsed fields
-  data["name"] = data["name"].title()
-  data["date"] = datetime.strptime(data["date"], '%Y-%m-%d')
-  data["duration"] = duration2secs(data["duration"])
-  data["description"] = [el.strip() for el in data["description"].split("::")]
-
-  return data
-
-def parse(content):
-  # split content at newlines
-  lines = content.split("\n")
-  out = []
-  for i, line in enumerate(lines):
-    line = line.strip()
-    if line.startswith("#"): continue
-    if len(line) == 0: continue
-
-    try: out.append(line2data(line))
-    except Exception as e: raise Exception(f"line {i+1}: {e}")
-  return out
-
-def fmt(times):
-  return f"""
-\\section{{Overviews}}\n
-\\subsection{{Members}}\n
-{fmt_member_overview(times)}
-\\subsection{{Weekly}}\n
-{fmt_weekly_overview(times)}
-"""
-
-def main(input_file):
-  content = ""
-  with open(input_file, "r") as file:
-    content = file.read()
-
-  try: parsed = parse(content)
-  except Exception as e:
-    print(f"{input_file}: {e}")
-    exit(1)
-  output = fmt(parsed)
-
-  output_file = input_file.removesuffix(".txt") + ".tex"
-  with open(output_file, "w+") as file:
-    file.write(output)
-
-if __name__ == "__main__":
-  if len(sys.argv) != 2:
-    print("usage: time2tex.py time.txt")
-    exit(1)
-  main(sys.argv[1])
-
-- 
cgit v1.2.3


From b31ebef3db3765eef8e0492897e870a9fa4cd32b Mon Sep 17 00:00:00 2001
From: Loek Le Blansch <loek@pipeframe.xyz>
Date: Mon, 16 Sep 2024 18:48:23 +0200
Subject: fix cross-reference links to external file

---
 example.tex         |  4 ++--
 projdoc.cls         |  1 +
 readme.md           |  6 ++++++
 requirements.tex    |  1 -
 scripts/reqs2tex.py | 13 +++++--------
 5 files changed, 14 insertions(+), 11 deletions(-)

(limited to 'projdoc.cls')

diff --git a/example.tex b/example.tex
index ee1377a..e0d21c0 100644
--- a/example.tex
+++ b/example.tex
@@ -3,7 +3,7 @@
 % with the [draft] option. this replaces all images with placeholders.
 \input{meta.tex}
 
-\input{reqs.aux}
+\externaldocument{reqs}[./requirements.pdf]
 
 \title{Example Document}
 
@@ -174,7 +174,7 @@ the glossary that is automatically printed after \codeinline{\end{document}}.
 \subsubsection{Requirements}
 
 Requirements are referenced like \codeinline{\label}s:
-e.g.~\cref{req:audio:handle:id,req:audio:async-api:id}.
+e.g.~\cref{req:audio:handle,req:audio:async-api}.
 
 \end{document}
 
diff --git a/projdoc.cls b/projdoc.cls
index c11fe61..8a592e3 100644
--- a/projdoc.cls
+++ b/projdoc.cls
@@ -40,6 +40,7 @@
 \RequirePackage{tabularx}
 \RequirePackage{booktabs}
 \RequirePackage{needspace}
+\RequirePackage{xr-hyper}
 \RequirePackage{hyperref}
 \RequirePackage{microtype}
 \RequirePackage{xcolor}
diff --git a/readme.md b/readme.md
index 818d445..7b58cfd 100644
--- a/readme.md
+++ b/readme.md
@@ -18,6 +18,12 @@ A `latexmkrc` file is provided for copmilation with latexmk. The documents
 should also compile under [Visual Studio Code][vscode] using the [LaTeX
 Workshop extension][latexworkshop], as well as [VimTeX][vimtex].
 
+## TODO
+
+- Requirement cross-references are broken (they print both the label and the
+  path to the other document, should be label only). Interesting:
+  `\creflabelformat` and `\@templabel` (inside #2 of `\creflabelformat`).
+
 [vscode]: https://code.visualstudio.com
 [latexworkshop]: https://marketplace.visualstudio.com/items?itemName=James-Yu.latex-workshop
 [vimtex]: https://github.com/lervag/vimtex
diff --git a/requirements.tex b/requirements.tex
index 39e5831..1b51220 100644
--- a/requirements.tex
+++ b/requirements.tex
@@ -6,6 +6,5 @@
 
 \begin{document}
 
-
 \end{document}
 
diff --git a/scripts/reqs2tex.py b/scripts/reqs2tex.py
index c5ab3dd..667eeb6 100755
--- a/scripts/reqs2tex.py
+++ b/scripts/reqs2tex.py
@@ -69,18 +69,15 @@ def convert(data):
   return reqs
 
 def req2aux(req):
-  # TODO: this is a dead-end solution, newlabel only works for in-document anchors, not external links
+  ref = tex.label2ref(req['label'])
   out = [
-    tex.scmd('newlabel', f"req:{req['label']}:id", tex.group(req['id'], req['id'], '', './requirements.pdf', '')),
-    tex.scmd('newlabel', f"req:{req['label']}:id@cref", tex.group(f"[requirement][][]{req['id']}", '')),
+    tex.cmd('newlabel', f"{ref}", tex.group(req['id'], req['id'], 'ggg', 'hhh', 'iii')),
+    tex.cmd('newlabel', f"{ref}@cref", tex.group(f"[requirement][aaa][bbb]{req['id']}", '[ccc][ddd][eee]fff')),
   ]
-  return "\n".join([tex.auxout(line) for line in out])
+  return "\n".join(out)
 
 def fmt_aux(data):
-  out = ""
-  out += tex.cmd('makeatletter')
-  out += "\n".join([req2aux(req) for req in data])
-  out += tex.cmd('makeatother')
+  out = "\n".join([req2aux(req) for req in data])
   return out
 
 def fmt_tex(data):
-- 
cgit v1.2.3


From 2e49e0e0db184295eb08e930a3ccdf10e80e40fe Mon Sep 17 00:00:00 2001
From: Loek Le Blansch <loek@pipeframe.xyz>
Date: Mon, 16 Sep 2024 19:07:50 +0200
Subject: implement simple requirements dump

---
 glossary.bib        |  5 +++++
 projdoc.cls         | 25 +++++++++++++++----------
 requirements.tex    | 12 +++++++++++-
 scripts/reqs2tex.py | 32 +++++++++++++++++++-------------
 scripts/tex.py      |  8 +++++++-
 5 files changed, 57 insertions(+), 25 deletions(-)

(limited to 'projdoc.cls')

diff --git a/glossary.bib b/glossary.bib
index 4d02e4a..437db86 100644
--- a/glossary.bib
+++ b/glossary.bib
@@ -28,3 +28,8 @@
 	description = {Graphics library developed by \hbox{Microsoft}},
 }
 
+@acronym{api,
+	short = {API},
+	long = {Application Programming Interface},
+}
+
diff --git a/projdoc.cls b/projdoc.cls
index 8a592e3..fe8c8bc 100644
--- a/projdoc.cls
+++ b/projdoc.cls
@@ -121,16 +121,23 @@
 	itemsep=\dimexpr\style@itemsep-\style@parsep\relax,
 	parsep=\style@parsep,
 }
-\def\projdoc@setdescriptionstyle{%
+\def\projdoc@description@before{%
 	\renewcommand\makelabel[1]{%
 		{\bfseries ##1}:%
 	}%
 }
-\setdescription{
-	before={\projdoc@setdescriptionstyle},
-	leftmargin=3em,
-	labelindent=3ex,
+\newlength\projdoc@description@leftmargin%
+\projdoc@description@leftmargin=3em%
+\newlength\projdoc@description@labelindent%
+\projdoc@description@labelindent=3ex%
+\def\projdoc@setdescriptionstyle{%
+	\setdescription{
+		before={\projdoc@description@before},
+		leftmargin=\projdoc@description@leftmargin,
+		labelindent=\projdoc@description@labelindent,
+	}%
 }
+\projdoc@setdescriptionstyle%
 \makeatother
 
 % create a label using \customlabel[<creftype>]{<refname>}{<reftext>} that displays
@@ -230,11 +237,9 @@
 	}{}%
 	% glossary
 	\ifbool{projdoc@used@gls}{%
-		\setdescription{
-			before={\projdoc@setdescriptionstyle},
-			leftmargin=2ex,
-			labelindent=0pt,
-		}%
+		\projdoc@description@leftmargin=2ex%
+		\projdoc@description@labelindent=0pt%
+		\projdoc@setdescriptionstyle%
 		\section*{Glossary}%
 		\begin{multicols}{2}%
 			\renewcommand{\glossarysection}[2][]{}%
diff --git a/requirements.tex b/requirements.tex
index 1b51220..dee529d 100644
--- a/requirements.tex
+++ b/requirements.tex
@@ -1,10 +1,20 @@
 \documentclass{projdoc}
 \input{meta.tex}
-\input{reqs.tex}
+
+\makeatletter
+\projdoc@description@leftmargin=2ex
+\projdoc@description@labelindent=0pt
+\projdoc@setdescriptionstyle
+\makeatother
 
 \title{Requirements}
 
 \begin{document}
 
+\section{Requirements}
+\begin{multicols}{2}
+\input{reqs.tex}
+\end{multicols}
+
 \end{document}
 
diff --git a/scripts/reqs2tex.py b/scripts/reqs2tex.py
index 667eeb6..3bf0501 100755
--- a/scripts/reqs2tex.py
+++ b/scripts/reqs2tex.py
@@ -68,22 +68,28 @@ def convert(data):
 
   return reqs
 
-def req2aux(req):
-  ref = tex.label2ref(req['label'])
-  out = [
-    tex.cmd('newlabel', f"{ref}", tex.group(req['id'], req['id'], 'ggg', 'hhh', 'iii')),
-    tex.cmd('newlabel', f"{ref}@cref", tex.group(f"[requirement][aaa][bbb]{req['id']}", '[ccc][ddd][eee]fff')),
-  ]
-  return "\n".join(out)
-
 def fmt_aux(data):
-  out = "\n".join([req2aux(req) for req in data])
-  return out
+  out = []
+  for req in data:
+    ref = tex.label2ref(req['label'])
+    out += [
+      tex.cmd('newlabel', f"{ref}", tex.group(req['id'], req['id'], 'ggg', 'hhh', 'iii')),
+      tex.cmd('newlabel', f"{ref}@cref", tex.group(f"[requirement][aaa][bbb]{req['id']}", '[ccc][ddd][eee]fff')),
+    ]
+  return "\n".join(out)
 
 def fmt_tex(data):
-  return "\n".join([
-    tex.cmd('relax')
-  ])
+  out = []
+  for req in data:
+    out.append(
+      tex.cmd('subsection', req['id']) + "\n\n" +\
+      tex.env('description',
+        tex.cmd('item', ['Priority']) + req['priority'].title() +\
+        tex.cmd('item', ['Requirement']) + req['description'] +\
+        (tex.cmd('item', ['Definition of done']) + req['done'] if req['type'] == 'user' else "")
+      )
+    )
+  return "\n\n".join(out)
 
 def main(input_file):
   data = {}
diff --git a/scripts/tex.py b/scripts/tex.py
index 2fd51d8..2509a87 100644
--- a/scripts/tex.py
+++ b/scripts/tex.py
@@ -1,7 +1,13 @@
 # utility function for converting latex code
 
 def group(*args):
-  return "".join("{" + arg + "}" for arg in args)
+  out = ""
+  for arg in args:
+    if isinstance(arg, list):
+      out += "[" + arg[0] + "]"
+    if isinstance(arg, str):
+      out += "{" + arg + "}"
+  return out
 
 def string(content):
   return r"\string" + content
-- 
cgit v1.2.3


From 6b034454f35819999cc26cfe472d537bf1eb3fbf Mon Sep 17 00:00:00 2001
From: Loek Le Blansch <loek@pipeframe.xyz>
Date: Wed, 18 Sep 2024 13:40:39 +0200
Subject: fix requirement cross-references

---
 example.tex         |  2 --
 projdoc.cls         | 83 +++++++++++++++++++++++++++++++++++++++++++++++------
 readme.md           |  6 ----
 scripts/reqs2tex.py | 16 +++++++++--
 4 files changed, 88 insertions(+), 19 deletions(-)

(limited to 'projdoc.cls')

diff --git a/example.tex b/example.tex
index e0d21c0..24c525b 100644
--- a/example.tex
+++ b/example.tex
@@ -3,8 +3,6 @@
 % with the [draft] option. this replaces all images with placeholders.
 \input{meta.tex}
 
-\externaldocument{reqs}[./requirements.pdf]
-
 \title{Example Document}
 
 \begin{document}
diff --git a/projdoc.cls b/projdoc.cls
index fe8c8bc..23f3ea9 100644
--- a/projdoc.cls
+++ b/projdoc.cls
@@ -145,13 +145,13 @@
 % \cref{<refname>} is used
 \makeatletter
 \NewDocumentCommand{\customlabel}{omm}{%
-  \begingroup
-  \cref@constructprefix{#1}{\cref@result}%
-  \protected@edef\@currentlabel{#3}%
-  \protected@edef\@currentlabelname{#3}%
-  \protected@edef\cref@currentlabel{[#1][][\cref@result]#3}
-  \label[#1]{#2}%
-  \endgroup
+	\begingroup
+	\cref@constructprefix{#1}{\cref@result}%
+	\protected@edef\@currentlabel{#3}%
+	\protected@edef\@currentlabelname{#3}%
+	\protected@edef\cref@currentlabel{[#1][][\cref@result]#3}
+	\label[#1]{#2}%
+	\endgroup
 }
 \makeatother
 
@@ -215,6 +215,9 @@
 	selection={recorded and deps and see},
 ]
 
+% requirements
+\externaldocument{reqs}[./requirements.pdf]
+
 % default document header/trailer
 \makeatletter
 \def\projdoc@header{
@@ -293,8 +296,8 @@
 	\def\UrlRight{\hbox{\,}}%
 }
 \DefineVerbatimEnvironment{blockcode}{Verbatim}{
-  tabsize=2,
-  obeytabs,
+	tabsize=2,
+	obeytabs,
 }
 
 % scale down image if it exceeds page margins
@@ -321,3 +324,65 @@
 \crefname{test}{test}{tests}
 \Crefname{test}{Test}{Tests}
 
+% fix cleveref showing filename to external cross-reference
+% see <https://tex.stackexchange.com/a/708445/211562>
+% edited from cleveref source
+\makeatletter
+\def\cref@getref#1#2{%
+	\expandafter\let\expandafter#2\csname r@#1@cref\endcsname%
+	\expandafter\expandafter\expandafter\def%
+		\expandafter\expandafter\expandafter#2%
+		\expandafter\expandafter\expandafter{%
+			\expandafter\@firstoffive#2}}%
+\def\cpageref@getref#1#2{%
+	\expandafter\let\expandafter#2\csname r@#1@cref\endcsname%
+	\expandafter\expandafter\expandafter\def%
+		\expandafter\expandafter\expandafter#2%
+		\expandafter\expandafter\expandafter{%
+			\expandafter\@secondoffive#2}}%
+\AtBeginDocument{%
+	\def\label@noarg#1{%
+		\cref@old@label{#1}%
+		\@bsphack%
+		\edef\@tempa{{page}{\the\c@page}}%
+		\setcounter{page}{1}%
+		\edef\@tempb{\thepage}%
+		\expandafter\setcounter\@tempa%
+		\cref@constructprefix{page}{\cref@result}%
+		\protected@write\@auxout{}{%
+			\string\newlabel{#1@cref}{%
+				{\cref@currentlabel}%
+				{[\@tempb][\arabic{page}][\cref@result]\thepage}%
+				{}%
+				{}%
+				{}%
+			}%
+		}%
+		\@esphack%
+	}%
+	\def\label@optarg[#1]#2{%
+		\cref@old@label{#2}%
+		\@bsphack%
+		\edef\@tempa{{page}{\the\c@page}}%
+		\setcounter{page}{1}%
+		\edef\@tempb{\thepage}%
+		\expandafter\setcounter\@tempa%
+		\cref@constructprefix{page}{\cref@result}%
+		\protected@edef\cref@currentlabel{%
+			\expandafter\cref@override@label@type%
+			\cref@currentlabel\@nil{#1}%
+		}%
+		\protected@write\@auxout{}{%
+			\string\newlabel{#2@cref}{%
+				{\cref@currentlabel}%
+				{[\@tempb][\arabic{page}][\cref@result]\thepage}%
+				{}%
+				{}%
+				{}%
+			}%
+		}%
+		\@esphack%
+	}%
+}
+\makeatother
+
diff --git a/readme.md b/readme.md
index 7b58cfd..818d445 100644
--- a/readme.md
+++ b/readme.md
@@ -18,12 +18,6 @@ A `latexmkrc` file is provided for copmilation with latexmk. The documents
 should also compile under [Visual Studio Code][vscode] using the [LaTeX
 Workshop extension][latexworkshop], as well as [VimTeX][vimtex].
 
-## TODO
-
-- Requirement cross-references are broken (they print both the label and the
-  path to the other document, should be label only). Interesting:
-  `\creflabelformat` and `\@templabel` (inside #2 of `\creflabelformat`).
-
 [vscode]: https://code.visualstudio.com
 [latexworkshop]: https://marketplace.visualstudio.com/items?itemName=James-Yu.latex-workshop
 [vimtex]: https://github.com/lervag/vimtex
diff --git a/scripts/reqs2tex.py b/scripts/reqs2tex.py
index 700d05f..8a6976a 100755
--- a/scripts/reqs2tex.py
+++ b/scripts/reqs2tex.py
@@ -101,8 +101,20 @@ def fmt_aux(data):
   for item in data:
     ref = label2ref(item[KEY.LABEL])
     out += [
-      tex.cmd('newlabel', f"{ref}", tex.group(item[KEY.ID], item[KEY.ID], 'ggg', 'hhh', 'iii')),
-      tex.cmd('newlabel', f"{ref}@cref", tex.group(f"[requirement][aaa][bbb]{item[KEY.ID]}", '[ccc][ddd][eee]fff')),
+      tex.cmd('newlabel', f"{ref}", tex.group(
+        item[KEY.ID],
+        '',
+        '',
+        ref,
+        '',
+      )),
+      tex.cmd('newlabel', f"{ref}@cref", tex.group(
+        f"[requirement][][]{item[KEY.ID]}",
+        '[][][]',
+        '',
+        '',
+        './requirements.pdf',
+      )),
     ]
   return "\n".join(out)
 
-- 
cgit v1.2.3


From f2e1083970107994a031a394198fde039bdf3b77 Mon Sep 17 00:00:00 2001
From: Loek Le Blansch <loek@pipeframe.xyz>
Date: Wed, 18 Sep 2024 14:10:59 +0200
Subject: cleanup

---
 projdoc.cls         | 2 +-
 scripts/reqs2tex.py | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

(limited to 'projdoc.cls')

diff --git a/projdoc.cls b/projdoc.cls
index 23f3ea9..7420d38 100644
--- a/projdoc.cls
+++ b/projdoc.cls
@@ -216,7 +216,7 @@
 ]
 
 % requirements
-\externaldocument{reqs}[./requirements.pdf]
+\externaldocument{reqs}[requirements.pdf]
 
 % default document header/trailer
 \makeatletter
diff --git a/scripts/reqs2tex.py b/scripts/reqs2tex.py
index 8a6976a..82b0aae 100755
--- a/scripts/reqs2tex.py
+++ b/scripts/reqs2tex.py
@@ -113,7 +113,7 @@ def fmt_aux(data):
         '[][][]',
         '',
         '',
-        './requirements.pdf',
+        '',
       )),
     ]
   return "\n".join(out)
-- 
cgit v1.2.3


From 0027f5df316892f121bb9f4b5b6b641646273ff0 Mon Sep 17 00:00:00 2001
From: Loek Le Blansch <loek@pipeframe.xyz>
Date: Wed, 18 Sep 2024 14:54:48 +0200
Subject: add requirements + improve generated reqs.tex

---
 projdoc.cls         |  7 +++++--
 reqs.toml           | 39 ++++++++++++++++++++++++++++++---------
 requirements.tex    |  1 +
 scripts/reqs2tex.py | 15 ++++++++-------
 scripts/tex.py      | 10 ++++++++++
 5 files changed, 54 insertions(+), 18 deletions(-)

(limited to 'projdoc.cls')

diff --git a/projdoc.cls b/projdoc.cls
index 7420d38..fccf8c1 100644
--- a/projdoc.cls
+++ b/projdoc.cls
@@ -215,8 +215,11 @@
 	selection={recorded and deps and see},
 ]
 
-% requirements
-\externaldocument{reqs}[requirements.pdf]
+% allow cross-references to requirements.pdf from all documents except
+% requirements.pdf itself
+\IfEq*{\jobname}{requirements}{}{
+	\externaldocument{reqs}[requirements.pdf]
+}
 
 % default document header/trailer
 \makeatletter
diff --git a/reqs.toml b/reqs.toml
index c05cf71..6645ea4 100644
--- a/reqs.toml
+++ b/reqs.toml
@@ -10,33 +10,46 @@
 # (cross-)referenced from LaTeX by prefixing this ID with `req:` and
 # substituting dots for colons (i.e. this requirement is referenced as
 # \cref{req:audio:async-api}).
-[audio.async-api]
+[audio]
 # Requirement type ('system' | 'user')
-type = 'system'
+type = 'user'
 # MoSCoW priority ('must' | 'should' | 'could' | 'will not')
 priority = 'must'
 # Requirement body. Supports LaTeX formatting. (tip: use single quotes so
 # backslash doesn't act as an escape character)
 description = '''
-The public audio \gls{api} supports starting audio samples asynchronously
-(i.e.~fire and forget).
+The engine allows the game programmer to easily start, pause and stop
+background music, while simultaniously playing sound effects.
 '''
-# Definition of done (user requirements only). If 'done' is a string, it is
-# treated as LaTeX code (like description), if it is a list of strings, each
-# item is treated as the ID of another requirement.
+# Definition of done. If 'done' is a string, it is treated as LaTeX code, if it
+# is a list of strings, each item is treated as the ID of another requirement,
+# and the references are checked before LaTeX runs.
+done = [
+	'audio.async-api',
+	'audio.handle',
+	'audio.stream-mix',
+	'audio.volume',
+]
 #done = 'When I feel like it'
-#done = [ 'audio.handle', 'audio.stream-mix' ]
 # Requirements that are no longer applicable should set `deleted` to `true`.
 # This will make sure the requirements are numbered consistently across
 # different document revisions.
 #deleted = true
 
+[audio.async-api]
+type = 'system'
+priority = 'must'
+description = '''
+The public audio \gls{api} supports starting audio samples asynchronously
+(i.e.~fire and forget).
+'''
+
 [audio.handle]
 type = 'system'
 priority = 'must'
 description = '''
 The public audio \gls{api} allows the game programmer to control (i.e.~play,
-pause and stop) audio samples after they are created/initialized.
+pause, resume and stop) audio samples after they are created/initialized.
 '''
 
 [audio.stream-mix]
@@ -46,6 +59,14 @@ description = '''
 The audio system supports playing multiple audio streams simultaniously.
 '''
 
+[audio.volume]
+type = 'system'
+priority = 'must'
+description = '''
+The public audio \gls{api} allows the game programmer to control the volume of
+audio samples.
+'''
+
 [aux.license]
 type = 'system'
 priority = 'must'
diff --git a/requirements.tex b/requirements.tex
index 2936272..cbaba81 100644
--- a/requirements.tex
+++ b/requirements.tex
@@ -6,6 +6,7 @@
 \projdoc@description@labelindent=0pt
 \projdoc@setdescriptionstyle
 \makeatother
+\setcounter{secnumdepth}{1}
 
 \title{Requirements}
 
diff --git a/scripts/reqs2tex.py b/scripts/reqs2tex.py
index 82b0aae..ff9f3bb 100755
--- a/scripts/reqs2tex.py
+++ b/scripts/reqs2tex.py
@@ -122,7 +122,7 @@ def fmt_tex(data):
   out = ""
   for item in data:
     out += tex.join(
-      tex.cmd('subsection', item[KEY.ID]),
+      tex.cmd('subsection', f"{item[KEY.ID]}: {item[KEY.LABEL]}".upper()),
       tex.withatletter(
         tex.cmd('cref@constructprefix', 'requirement', r'\cref@result'),
         tex.pedef('@currentlabel', item[KEY.ID]),
@@ -130,12 +130,13 @@ def fmt_tex(data):
         tex.pedef('cref@currentlabel', tex.group(['requirement'], [''], [r'\cref@result']) + item[KEY.ID]),
       ),
       tex.cmd('label', ['requirement'], label2ref(item[KEY.LABEL])),
-      tex.cmd('par'),
-      tex.env('description', tex.join(
-        tex.cmd('item', ['Priority']) + item[KEY.PRIORITY].title(),
-        tex.cmd('item', ['Requirement']) + item[KEY.DESCRIPTION],
-        (tex.cmd('item', ['Definition of done']) + item[KEY.DONE] if item[KEY.DONE] is not None else ""),
-      ))
+      tex.cmd('parbox', tex.cmd('linewidth'),
+        tex.env('description', tex.join(
+          tex.cmd('item', ['Priority']) + item[KEY.PRIORITY].title(),
+          tex.cmd('item', ['Requirement']) + item[KEY.DESCRIPTION],
+          (tex.cmd('item', ['Definition of done']) + item[KEY.DONE] if item[KEY.DONE] is not None else ""),
+        )),
+      )
     )
   return out
 
diff --git a/scripts/tex.py b/scripts/tex.py
index e8fc65b..07d275a 100644
--- a/scripts/tex.py
+++ b/scripts/tex.py
@@ -69,3 +69,13 @@ def explist(*items):
       out += explist(*item)
   return out
 
+def sec(level, heading):
+  level = max(min(3, level), 0)
+  section = [
+    'section',
+    'subsection',
+    'subsubsection',
+    'paragraph',
+  ][level]
+  return cmd(section, heading)
+
-- 
cgit v1.2.3


From c37f21e48f17abe9fb6bf1549f680e6f730aed8c Mon Sep 17 00:00:00 2001
From: Loek Le Blansch <loek@pipeframe.xyz>
Date: Wed, 18 Sep 2024 16:52:40 +0200
Subject: WIP research

---
 projdoc.cls         |  3 +++
 reqs.toml           | 16 ++++++++++-
 research.tex        | 34 ++++++++++-------------
 scripts/reqs2tex.py |  3 +++
 sources.bib         | 77 +++++++++++++++++++++++++++++++++++++++--------------
 5 files changed, 92 insertions(+), 41 deletions(-)

(limited to 'projdoc.cls')

diff --git a/projdoc.cls b/projdoc.cls
index fccf8c1..05e401c 100644
--- a/projdoc.cls
+++ b/projdoc.cls
@@ -389,3 +389,6 @@
 }
 \makeatother
 
+% missing reference marker
+\def\mref{\textsuperscript{\textit{\,ref?}}}
+
diff --git a/reqs.toml b/reqs.toml
index 6645ea4..2da91ac 100644
--- a/reqs.toml
+++ b/reqs.toml
@@ -67,7 +67,9 @@ The public audio \gls{api} allows the game programmer to control the volume of
 audio samples.
 '''
 
-[aux.license]
+# TODO: audio encoding support?
+
+[lib.license]
 type = 'system'
 priority = 'must'
 description = '''
@@ -75,3 +77,15 @@ External libraries must have a license that is MIT-compatible, or one that
 allows linking against MIT code.
 '''
 
+[lib.platform]
+type = 'system'
+priority = 'must'
+description = '''
+External libraries must have cross-platform support for at least Linux and
+Windows.
+'''
+
+# TODO: library popularity as quality factor?
+# TODO: library documentation as quality factor?
+# TODO: modularity over less libraries? (i.e. why don't we just SDL2 everything?)
+
diff --git a/research.tex b/research.tex
index ad33d10..abfde3b 100644
--- a/research.tex
+++ b/research.tex
@@ -201,44 +201,38 @@ for audio some options could be: FMOD, Wwise, or iirKlang
 
 \subsection{Conclusion}
 
-% TODO: this entire section
 \section{Audio}
 
-% should audio research be scoped down to SDL2 (if that's what we're going with) or
-% standalone libraries only (for modularity?).
-
-The game engine is required to have an audio system with support for playing multiple
-audio streams (i.e.~tracks or samples) simultaniously. Since writing a custom live
-audio mixing engine is outside the scope of this project, this section compares
+The game engine is required to have an audio system
+\autocite[\ref{req:audio}]{crepe:requirements}. Since writing a custom real-time
+audio mixing engine is outside the scope of this project\mref, this section compares
 various standalone audio engines that could be used in the engine.
 
-% TODO: requirements first!
+\subsection{Libraries}
+
+\Cref{tab:audio-engines} compares several standalone audio engine libraries that fit
+\cref{req:audio,req:lib:license}.
 
-% REQ ~ is cross-platform
-% REQ ~ supports multiple audio formats (TODO: which)
-% REQ ~ supports simultanious playback / mixing
-% REQ ~ has an open-source license
 \begin{table}
 	\centering
 	\begin{tabular}{llc}
 		\toprule
 		\textbf{Library} & \textbf{License} & \textbf{API}\\
 		\midrule
-		miniaudio & MIT-0 & C\\
-		YSE & EPL & C++\\
-		SoLoud & Zlip/LibPng & C++\\
+		miniaudio \autocite{lib:miniaudio} & MIT-0 & C\\
+		YSE \autocite{lib:yse} & EPL & C++\\
+		SoLoud \autocite{lib:soloud} & Zlip/LibPng & C++\\
 		\bottomrule
 	\end{tabular}
 	\caption{Audio engine library comparison}
 	\label{tab:audio-engines}
 \end{table}
-% TODO: ref https://miniaud.io/
-% TODO: ref https://www.attr-x.net/yse/
 
-Not considered further:
+Other popular libraries that were researched but are unsuitable for this project
+include:\noparbreak
 \begin{description}
-	\item[FMOD] is proprietary
-	\item[PortAudio] requires manual mixing
+	\item[FMOD \autocite{lib:fmod}] Is proprietary (violates \cref{req:lib:license})
+	\item[PortAudio \autocite{lib:portaudio}] Does not handle mixing
 \end{description}
 
 \section{Physics}
diff --git a/scripts/reqs2tex.py b/scripts/reqs2tex.py
index e5f063d..6984466 100755
--- a/scripts/reqs2tex.py
+++ b/scripts/reqs2tex.py
@@ -94,6 +94,9 @@ def convert(data):
   # skip deleted requirements (but process for make_id)
   reqs = [item for item in reqs if item[KEY.DELETED] == False]
 
+  # sort by label
+  reqs = sorted(reqs, key=lambda item: item[KEY.LABEL])
+
   return reqs
 
 def fmt_aux(data):
diff --git a/sources.bib b/sources.bib
index 50f5ead..6f5ce0c 100644
--- a/sources.bib
+++ b/sources.bib
@@ -13,38 +13,75 @@
 }
 
 @misc{miro:scrum-board,
-  author = {Loek Le Blansch and Wouter Boerenkamps and Jaro Rutjes and Max Smits and Niels Stunnebrink},
-  title = {Scrum Board on Miro},
-  url = {https://miro.com/app/board/uXjVKjtdM64=/?share_link_id=303851465474},
-  date = {2024-09-10},
+	author = {Loek Le Blansch and Wouter Boerenkamps and Jaro Rutjes and Max Smits and Niels Stunnebrink},
+	title = {Scrum Board on Miro},
+	url = {https://miro.com/app/board/uXjVKjtdM64=/?share_link_id=303851465474},
+	date = {2024-09-10},
 }
 
 @misc{crepe:code-repo,
-  author = {Loek Le Blansch and Wouter Boerenkamps and Jaro Rutjes and Max Smits and Niels Stunnebrink},
-  title = {Crepe Code Repository},
-  url = {https://github.com/lonkaars/crepe},
-  date = {2024-09-10},
+	author = {Loek Le Blansch and Wouter Boerenkamps and Jaro Rutjes and Max Smits and Niels Stunnebrink},
+	title = {Crepe Code Repository},
+	url = {https://github.com/lonkaars/crepe},
+	date = {2024-09-10},
 }
 
 @misc{crepe:docs-repo,
-  author = {Loek Le Blansch and Wouter Boerenkamps and Jaro Rutjes and Max Smits and Niels Stunnebrink},
-  title = {Crepe Documentation Repository},
-  url = {https://github.com/lonkaars},
-  date = {2024-09-10},
+	author = {Loek Le Blansch and Wouter Boerenkamps and Jaro Rutjes and Max Smits and Niels Stunnebrink},
+	title = {Crepe Documentation Repository},
+	url = {https://github.com/lonkaars},
+	date = {2024-09-10},
 }
 
 @misc{crepe:docs-standard,
-  author = {Loek Le Blansch and Wouter Boerenkamps and Jaro Rutjes and Max Smits and Niels Stunnebrink},
-  title = {Crepe Documentation Standard},
-  url = {https://github.com/lonkaars/crepe-docs/blob/master/contributing.md},
-  date = {2024-09-10},
+	author = {Loek Le Blansch and Wouter Boerenkamps and Jaro Rutjes and Max Smits and Niels Stunnebrink},
+	title = {Crepe Documentation Standard},
+	url = {https://github.com/lonkaars/crepe-docs/blob/master/contributing.md},
+	date = {2024-09-10},
 }
 
 @misc{crepe:code-standard,
-  author = {Loek Le Blansch and Wouter Boerenkamps and Jaro Rutjes and Max Smits and Niels Stunnebrink},
-  title = {Crepe Code Standard},
-  url = {https://github.com/lonkaars/crepe/blob/master/contributing.md},
-  date = {2024-09-10},
+	author = {Loek Le Blansch and Wouter Boerenkamps and Jaro Rutjes and Max Smits and Niels Stunnebrink},
+	title = {Crepe Code Standard},
+	url = {https://github.com/lonkaars/crepe/blob/master/contributing.md},
+	date = {2024-09-10},
 }
 
+@report{crepe:requirements,
+	author = {Loek Le Blansch and Wouter Boerenkamps and Jaro Rutjes and Max Smits and Niels Stunnebrink},
+	title = {Requirements},
+	date = {2024-09-18},
+}
+
+@online{lib:miniaudio,
+	title = {miniaudio - A single file audio playback and capture library.},
+	% author = {David Reid},
+	url = {https://miniaud.io},
+	urldate = {2024-09-18},
+}
+
+@online{lib:yse,
+	title = {YSE - cross-platform sound engine},
+	url = {https://www.attr-x.net/yse},
+	urldate = {2024-09-18},
+}
+
+@online{lib:soloud,
+	title = {SoLoud},
+	% author = {Jari Komppa},
+	url = {https://solhsa.com/soloud},
+	urldate = {2024-09-18},
+}
+
+@online{lib:fmod,
+	title = {FMOD},
+	url = {https://www.fmod.com},
+	urldate = {2024-09-18},
+}
+
+@online{lib:portaudio,
+	title = {PortAudio - an Open-Source Cross-Platform Audio API},
+	url = {https://www.portaudio.com},
+	urldate = {2024-09-18},
+}
 
-- 
cgit v1.2.3


From ea452a59151c1e9bae39aaee568a2d9c7bd006a2 Mon Sep 17 00:00:00 2001
From: Loek Le Blansch <loek@pipeframe.xyz>
Date: Wed, 18 Sep 2024 16:58:53 +0200
Subject: small fixes

---
 projdoc.cls      | 3 +++
 requirements.tex | 3 +--
 2 files changed, 4 insertions(+), 2 deletions(-)

(limited to 'projdoc.cls')

diff --git a/projdoc.cls b/projdoc.cls
index fccf8c1..fe6317b 100644
--- a/projdoc.cls
+++ b/projdoc.cls
@@ -75,6 +75,9 @@
 \bigskipamount=7mm
 \medskipamount=4mm
 
+% number paragraphs by default
+\setcounter{secnumdepth}{4}
+
 % section placement / appearance
 \newlength{\sectionpenalty}
 \newlength{\subsectionpenalty}
diff --git a/requirements.tex b/requirements.tex
index 78496e8..be0e103 100644
--- a/requirements.tex
+++ b/requirements.tex
@@ -8,7 +8,6 @@
 \makeatother
 \setcounter{secnumdepth}{1}
 
-\usepackage{bophook}
 \makeatletter
 \def\reqlabel#1{\csuse{reqlabel@#1}}
 \def\reqlabelreset{%
@@ -26,7 +25,7 @@
 	}%
 }
 % Abbreviate requirement field labels after first occurrence on every page
-\AtBeginPage{\reqlabelreset}
+\AtBeginShipout{\reqlabelreset}
 \makeatother
 
 \title{Requirements}
-- 
cgit v1.2.3


From b5ad864b5f020a73e4e0a068302cd22209f01fbb Mon Sep 17 00:00:00 2001
From: Loek Le Blansch <loek@pipeframe.xyz>
Date: Thu, 19 Sep 2024 17:27:21 +0200
Subject: remove version from documents

---
 contributing.md |  4 ----
 latexmkrc       |  2 +-
 meta.tex        |  1 -
 plan.tex        |  8 --------
 projdoc.cls     | 11 ++++++++---
 5 files changed, 9 insertions(+), 17 deletions(-)

(limited to 'projdoc.cls')

diff --git a/contributing.md b/contributing.md
index c11c834..db58388 100644
--- a/contributing.md
+++ b/contributing.md
@@ -2,10 +2,6 @@ This document is an extension of the [crêpe engine contribution
 guidelines][crepe-engine-contrib]. Rules in this document override those in the
 other document.
 
-# Versioning
-
-- TODO: discuss w/ group
-
 # Code style
 
 - Indent using tabs
diff --git a/latexmkrc b/latexmkrc
index 880f859..d2c3cdc 100644
--- a/latexmkrc
+++ b/latexmkrc
@@ -2,7 +2,7 @@
 
 use File::Spec::Functions;
 
-$pdflatex = "xelatex --interaction=nonstopmode %O %S";
+$pdflatex = "xelatex --interaction=nonstopmode --shell-escape %O %S";
 $pdf_mode = 1;
 $dvi_mode = 0;
 $postscript_mode = 0;
diff --git a/meta.tex b/meta.tex
index cad4b81..db7c7b1 100644
--- a/meta.tex
+++ b/meta.tex
@@ -1,6 +1,5 @@
 \organization{Avans University of Applied Sciences}
 \project{Project cr\^epe}
-\version{0.0}
 \author{%
 	Loek Le Blansch\and%
 	Wouter Boerenkamps\and%
diff --git a/plan.tex b/plan.tex
index 076ce11..6ebd73a 100644
--- a/plan.tex
+++ b/plan.tex
@@ -1,15 +1,7 @@
 \documentclass{projdoc}
 \input{meta.tex}
 
-% @Jaro: Zie hieronder
-% Version 0.0 seems not right
-% I'm also missing a 'version table'
-% Shall we use this version system?:
-% Version numbers after the comma indicate an interim version and version numbers
-% before the comma indicate versions ready for publication
-
 \title{Project Plan}
-\version{0.1}
 
 \begin{document}
 \tablestables
diff --git a/projdoc.cls b/projdoc.cls
index fe6317b..ee94835 100644
--- a/projdoc.cls
+++ b/projdoc.cls
@@ -164,8 +164,6 @@
 \def\project#1{\def\@project{#1}}
 \let\@organization\relax
 \def\organization#1{\def\@organization{#1}}
-\let\@version\relax
-\def\version#1{\def\@version{#1}}
 \def\@maketitle{%
 	\centering%
 	\parskip=0pt%
@@ -195,8 +193,15 @@
 	}%
 	\vfill\flushright%
 	\par{%
-		\par{\strut{}Version \@version\strut}%
 		\par{\strut\@date\strut}%
+		\begingroup%
+		\endlinechar=\m@ne\everyeof{\noexpand}%
+		\edef\x{%
+			\endgroup%
+			\def\noexpand\commit{\@@input|"git rev-parse --short HEAD" }%
+		}%
+		\x%
+		\par{\strut\footnotesize({\ttfamily\commit})\strut}%
 	}%
 	\par\vspace*{2in}%
 }
-- 
cgit v1.2.3


From 6329755a372d90985bda1cd164fae8d91d4a60c6 Mon Sep 17 00:00:00 2001
From: Loek Le Blansch <loek@pipeframe.xyz>
Date: Thu, 26 Sep 2024 15:50:58 +0200
Subject: remove meta.tex and just set defaults directly in projdoc.cls

---
 example.tex      |  1 -
 meta.tex         |  9 ---------
 plan.tex         |  1 -
 projdoc.cls      | 15 +++++++++++++--
 requirements.tex |  1 -
 research.tex     |  1 -
 timerep.tex      |  1 -
 7 files changed, 13 insertions(+), 16 deletions(-)
 delete mode 100644 meta.tex

(limited to 'projdoc.cls')

diff --git a/example.tex b/example.tex
index 24c525b..9f36f59 100644
--- a/example.tex
+++ b/example.tex
@@ -1,7 +1,6 @@
 \documentclass{projdoc}
 % if the document compiles too slow (likely due to many/large images), try compiling
 % with the [draft] option. this replaces all images with placeholders.
-\input{meta.tex}
 
 \title{Example Document}
 
diff --git a/meta.tex b/meta.tex
deleted file mode 100644
index db7c7b1..0000000
--- a/meta.tex
+++ /dev/null
@@ -1,9 +0,0 @@
-\organization{Avans University of Applied Sciences}
-\project{Project cr\^epe}
-\author{%
-	Loek Le Blansch\and%
-	Wouter Boerenkamps\and%
-	Jaro Rutjes\and%
-	Max Smits\and%
-	Niels Stunnebrink%
-}
diff --git a/plan.tex b/plan.tex
index c439aa7..a67598e 100644
--- a/plan.tex
+++ b/plan.tex
@@ -1,5 +1,4 @@
 \documentclass{projdoc}
-\input{meta.tex}
 
 \title{Project Plan}
 
diff --git a/projdoc.cls b/projdoc.cls
index 16b1790..8a2c05d 100644
--- a/projdoc.cls
+++ b/projdoc.cls
@@ -1,6 +1,19 @@
 \NeedsTeXFormat{LaTeX2e}
 \ProvidesClass{projdoc}[2024-09-03 class projdoc]
 
+% project defaults
+\makeatletter
+\def\@project{Project cr\^epe}
+\def\@organization{Avans University of Applied Sciences}
+\def\@author{%
+	Loek Le Blansch\and%
+	Wouter Boerenkamps\and%
+	Jaro Rutjes\and%
+	Max Smits\and%
+	Niels Stunnebrink%
+}
+\makeatother
+
 % based on article
 \LoadClass{article}
 
@@ -160,9 +173,7 @@
 
 % \maketitle format
 \makeatletter
-\let\@project\relax
 \def\project#1{\def\@project{#1}}
-\let\@organization\relax
 \def\organization#1{\def\@organization{#1}}
 \def\@maketitle{%
 	\centering%
diff --git a/requirements.tex b/requirements.tex
index be0e103..6b4a87a 100644
--- a/requirements.tex
+++ b/requirements.tex
@@ -1,5 +1,4 @@
 \documentclass{projdoc}
-\input{meta.tex}
 
 \makeatletter
 \projdoc@description@leftmargin=2ex
diff --git a/research.tex b/research.tex
index 228f3ac..5aa7c4f 100644
--- a/research.tex
+++ b/research.tex
@@ -1,5 +1,4 @@
 \documentclass{projdoc}
-\input{meta.tex}
 
 \title{Research document}
 
diff --git a/timerep.tex b/timerep.tex
index 7590217..34a30ea 100644
--- a/timerep.tex
+++ b/timerep.tex
@@ -1,5 +1,4 @@
 \documentclass{projdoc}
-\input{meta.tex}
 
 \title{Time Report}
 
-- 
cgit v1.2.3

