2024-01-28 19:40:08 +01:00

329 lines
10 KiB
Lua

--- Code related to LaTeX generation.
-- @module advtrains_doc_integration.latex
local latex = {}
local utils = advtrains_doc_integration.utils
--- Escape string in LaTeX.
-- @tparam string str The string to escape.
-- @treturn string The escaped string.
function latex.escape(str)
return (string.gsub(str, ".", {
["&"] = [[\&]],
["%"] = [[\%]],
["$"] = [[\$]],
["#"] = [[\#]],
["_"] = [[\_]],
["{"] = [[\{]],
["}"] = [[\}]],
["~"] = [[\textasciitilde]],
["^"] = [[\textasciicircum]],
["\\"] = [[\textbackslash]],
}))
end
--- Escape a translated string in LaTeX.
-- @tparam string lang The language to use for server-side translation
-- @tparam string str The string to escape.
-- @treturn The escaped string.
function latex.escape_translated(lang, str)
return latex.escape(minetest.get_translated_string(lang, str))
end
local function SL(str)
return latex.escape_translated("en", str)
end
--- Describe a color in LaTeX.
-- @tparam string cstr The color string.
-- @treturn string The string describing the color.
function latex.describe_color(cstr)
local color = string.match(cstr,"^#(%x%x%x%x%x%x)$")
cstr = SL(cstr)
if color then
color = SL(string.upper(color))
return string.format([[\tikz \definecolor{c}{HTML}{%s} \draw[fill=c] (0,0) rectangle (1em,1em); \texttt{%s}]], color, cstr)
else
return string.format([[\texttt{%s}]], cstr)
end
end
--- Describe a coupler in LaTeX.
-- @tparam string coupler The name of the coupler.
-- @treturn string The string describing the coupler.
function latex.describe_coupler(coupler)
local name = utils.get_coupler_name(coupler)
if name then
return ([[%s (\texttt{%s})]]):format(SL(name), SL(coupler))
else
return ([[\texttt{%s}]]):format(SL(coupler))
end
end
--- Describe a wagon prototype in LaTeX.
-- @tparam string itemname The item name of the wagon prototype.
-- @treturn string The description of the prototype.
function latex.describe_wagon_prototype(itemname)
local prototype = advtrains_doc_integration.prototypes[itemname]
local wname = ItemStack(itemname):get_short_description()
local st = {string.format([[
\documentclass{article}
\usepackage[a4paper,margin=1in,bottom=1.5in]{geometry}
\usepackage[T1]{fontenc}
\usepackage{tikz}
\usepackage{booktabs,multirow,tabularx}
\renewcommand{\arraystretch}{1.5}
\usepackage{hyperref}
\hypersetup{pdftitle={Wagon Datasheet: %s}}
\title{Wagon Datasheet}
\author{%s}
\setlength{\parindent}{0pt}
\begin{document}
\maketitle
]], SL(wname), SL(wname))}
table.insert(st, [[\section{Basic Information}]])
if prototype.longdesc then
table.insert(st, SL(prototype.longdesc) .. "\n")
end
table.insert(st, [[\begin{tabularx}{\textwidth}{l X}]])
table.insert(st, string.format([[Itemstring & \texttt{%s}\\]], SL(itemname)))
if prototype.drives_on then
local i0 = #st+1
local count = 0
for k in pairs(prototype.drives_on) do
table.insert(st, string.format([[& \texttt{%s}\\]], SL(k)))
count = count + 1
end
if count > 0 then
st[i0] = string.format([[Drives on %s]], st[i0])
end
end
if prototype.wagon_span then
table.insert(st, string.format([[Wagon span & %d mm\\]], prototype.wagon_span*2000))
end
if prototype.max_speed then
table.insert(st, string.format([[Maximum speed & %d m/s\\]], prototype.max_speed))
end
table.insert(st, string.format([[Motive power & %s\\]], prototype.is_locomotive and "Present" or "Absent"))
if prototype.horn_sound then
table.insert(st, string.format([[Horn sound & \texttt{%s}\\]], SL(prototype.horn_sound.name)))
else
table.insert(st, [[Horn sound & Undefined\\]])
end
if prototype.mesh then
table.insert(st, string.format([[Mesh & \texttt{%s}\\]], SL(prototype.mesh)))
end
if prototype.textures then
local i0 = #st+1
local count = 0
for _, i in pairs(prototype.textures) do
table.insert(st, string.format([[& \texttt{%s}\\]], SL(i)))
count = count + 1
end
if count > 0 then
st[i0] = string.format([[Textures %s]], st[i0])
end
end
do
local i0 = #st+1
local count = 0
for _, i in ipairs(prototype.drops or {}) do
local item = ItemStack(i)
if not item:is_empty() then
local desc = string.format([[\texttt{%s}]], SL(item:get_name()))
if item:is_known() then
desc = SL(item:get_short_description())
end
table.insert(st, string.format([[& %s: %d\\]], desc, item:get_count()))
count = count + 1
end
end
if count > 0 then
st[i0] = [[Drops ]] .. st[i0]
else
table.insert(st, [[Drops & Nothing \\]])
end
end
table.insert(st, [[\end{tabularx}]])
table.insert(st, [[\section{Coupler Compatibility}]])
do
local fcouplers = prototype.coupler_types_front
local rcouplers = prototype.coupler_types_back
local ccouplers = {}
local lcouplers = {}
local couplerid = {}
local flim, rlim
for k in pairs(fcouplers or {}) do
flim = true
ccouplers[k] = true
end
for k in pairs(rcouplers or {}) do
rlim = true
ccouplers[k] = true
end
for k in pairs(ccouplers) do
local desc = latex.describe_coupler(k)
table.insert(lcouplers, desc)
couplerid[desc] = k
end
table.sort(lcouplers)
table.insert(st, [[
\begin{tabularx}{\textwidth}{X c c}
\toprule
\multirow[t]{2}{*}{\bfseries Coupler Type} & \multicolumn{2}{c}{\bfseries Compatibility}\\
\cmidrule(lr){2-3}
& {\bfseries Front Coupler} & {\bfseries Rear Coupler}\\\midrule
]])
if not (fcouplers and rcouplers) then
local fd = fcouplers and "" or [[$\bullet$]]
local rd = rcouplers and "" or [[$\bullet$]]
table.insert(st, string.format([[\textit{Universal}&%s&%s\\]], fd, rd))
end
for i = 1, #lcouplers do
local cdesc = lcouplers[i]
local cid = couplerid[cdesc]
local fd, rd = "", ""
if flim then
fd = fcouplers[cid] and [[$\bullet$]] or ""
elseif not fcouplers then
fd = [[$\Uparrow$]]
end
if rlim then
rd = rcouplers[cid] and [[$\bullet$]] or ""
elseif not rcouplers then
rd = [[$\Uparrow$]]
end
table.insert(st, string.format([[%s&%s&%s\\]], cdesc, fd, rd))
end
table.insert(st, [[\bottomrule]])
table.insert(st, [[\end{tabularx}]])
end
local hasinv = prototype.has_inventory
local hasseats = prototype.max_seats>0
local taliquid = prototype.techage_liquid_capacity or 0
if hasinv or hasseats or taliquid>0 then
table.insert(st, [[\section{Wagon Capacity}]])
if hasseats then
table.insert(st, [[
\begin{tabularx}{\textwidth}{X c c}
\toprule
{\bfseries Seat Group} & {\bfseries Driver Stand} & {\bfseries Seat Count}\\\midrule
]])
for _, d in pairs(prototype.seat_groups) do
table.insert(st, string.format([[%s & %s & %d\\]], SL(d.name), d.driving_ctrl_access and [[$\bullet$]] or "", d.count))
end
table.insert(st, [[\bottomrule]])
table.insert(st, [[\end{tabularx}]])
end
if hasinv then
if next(prototype.inventory_list_sizes or {}) ~= nil then
table.insert(st, [[
\begin{tabularx}{\textwidth}{X c}
\toprule
{\bfseries Inventory Name} & {\bfseries Capacity}\\\midrule
]])
for k, v in pairs(prototype.inventory_list_sizes) do
table.insert(st, string.format([[\texttt{%s} & %d\\]], SL(k), v))
end
table.insert(st, [[\bottomrule]])
table.insert(st, [[\end{tabularx}]])
else
table.insert(st, [[This wagon has an inventory of unknown size.]])
end
end
if taliquid > 0 then
table.insert(st, string.format([[
\begin{tabularx}{\textwidth}{X l}
{Liquid Capacity (Techage)} & %d
\end{tabularx}
]], taliquid))
end
end
if prototype.set_livery then
if prototype.livery_definition then
table.insert(st, [[\section{Multi-Component Liveries}]])
local components = prototype.livery_definition.components
local presets = prototype.livery_definition.presets
table.insert(st, [[\subsection*{Components}]])
table.insert(st, [[\begin{itemize}]])
for _, c in ipairs(components) do
table.insert(st, string.format([[\item %s]], SL(c.description)))
end
table.insert(st, [[\end{itemize}]])
for _, p in ipairs(presets) do
table.insert(st, string.format([[\subsection*{Preset: %s}]], SL(p.description)))
table.insert(st, [[
\begin{tabularx}{\textwidth}{X c}
\toprule
{\bfseries Component} & {\bfseries Color} \\\midrule
]])
for _, c in ipairs(p.livery_stack.layers) do
local cdesc = SL(components[c.component].description)
table.insert(st, string.format([[%s & %s\\]], cdesc, latex.describe_color(c.color)))
end
table.insert(st, [[
\bottomrule
\end{tabularx}
]])
end
else
table.insert(st, [[\section{Livery System (Bike Painter)}]])
table.insert(st, [[This wagon can be painted by the bike painter.]])
end
end
local dlxlivdef = prototype.dlxtrains_livery
if dlxlivdef then
table.insert(st, [[
\section{DlxTrains Livery Sytem}
This wagon can be customized with DlxTrains' livery system.
]])
end
local atlivdef = prototype.advtrains_livery_tools
if atlivdef then
table.insert(st, [[\section{Advtrains Livery Tool (Marnack)}]])
for _, tname in ipairs(atlivdef.template_names) do
local tdef = atlivdef.templates[tname]
table.insert(st, string.format([[\subsection*{Template: %s}]], SL(tname)))
table.insert(st, SL(tdef.notes))
table.insert(st, "")
table.insert(st, "This template contains the following components:")
table.insert(st, [[\begin{itemize}]])
for _, overlay in ipairs(tdef.overlays) do
table.insert(st, string.format([[\item %s]], SL(overlay.name)))
end
table.insert(st, [[\end{itemize}]])
end
for _, lname in ipairs(atlivdef.livery_names) do
local ldef = atlivdef.liveries[lname]
local tname = ldef.livery_template_name
table.insert(st, string.format([[\subsection*{Preset: %s}]], SL(lname)))
table.insert(st, string.format([[Template: %s]], SL(tname)))
table.insert(st, "")
table.insert(st, [[
\begin{tabularx}{\textwidth}{X c}
\toprule
{\bfseries Component} & {\bfseries Color}\\\midrule]])
for _, overlay in pairs(ldef.overlays) do
local cname = atlivdef.templates[tname].overlays[overlay.id].name
table.insert(st, string.format([[%s & %s\\]], SL(cname), latex.describe_color(overlay.color)))
end
table.insert(st, [[
\bottomrule
\end{tabularx}
]])
end
end
table.insert(st, [[
\end{document}
]])
return table.concat(st, "\n")
end
return latex