458 lines
20 KiB
Nix
458 lines
20 KiB
Nix
# TODO: Remove LaTeX-Code, replace with renderer-methods
|
|
{ chunks, core, evaluator, renderer, ... }:
|
|
let
|
|
inherit(core) debug error indentation list number path set string type;
|
|
inherit(evaluator) evaluate;
|
|
inherit(renderer) putCaption toCaption toDescription render;
|
|
|
|
evaluateFigure
|
|
= { ... } @ document:
|
|
{ ... } @ state:
|
|
{ dependencies, ... } @ figure:
|
|
let
|
|
state' = state;
|
|
in
|
|
state'
|
|
// {
|
|
dependencies = state'.dependencies ++ dependencies;
|
|
figures
|
|
= state'.figures
|
|
// {
|
|
counter = state'.figures.counter + 1;
|
|
};
|
|
};
|
|
|
|
getOptions
|
|
= config:
|
|
( if config ? width then [ "width=${string config.width}" ] else [ ] )
|
|
++ ( if config ? height then [ "height=${string config.height}" ] else [ ] )
|
|
++ ( getOptions' config );
|
|
|
|
getOptions'
|
|
= config:
|
|
( if config ? totalheight then [ "totalheight=${string config.totalheight}" ] else [ ] )
|
|
++ ( if config.keepaspectratio or false then [ "keepaspectratio" ] else [ ] )
|
|
++ ( if config ? scale then [ "scale=${string config.scale}" ] else [ ] )
|
|
++ ( if config ? angle then [ "angle=${string config.angle}" ] else [ ] )
|
|
++ ( if config ? origin then [ "origin=${string config.origin}" ] else [ ] )
|
|
++ ( if config ? viewport then [ "viewport=${string config.viewport}" ] else [ ] )
|
|
++ ( if config ? trim then [ "trim=${string config.trim}" ] else [ ] )
|
|
++ ( if config.clip or false then [ "clip" ] else [ ] )
|
|
++ ( if config ? page then [ "page=${string config.page}" ] else [ ] )
|
|
++ ( if config ? pagebox then [ "pagebox=${string config.pagebox}" ] else [ ] )
|
|
++ ( if config.interpolate or false then [ "interpolate" ] else [ ] )
|
|
++ ( if config.quiet or false then [ "quiet" ] else [ ] )
|
|
++ ( if config.draft or false then [ "draft" ] else [ ] )
|
|
++ ( if config ? type then [ "type=${string config.type}" ] else [ ] )
|
|
++ ( if config ? ext then [ "ext=${string config.ext}" ] else [ ] )
|
|
++ ( if config ? read then [ "read=${string config.read}" ] else [ ] )
|
|
++ ( if config ? command then [ "command=${string config.command}" ] else [ ] )
|
|
++ ( config.extraOptions or [] );
|
|
|
|
orDefault
|
|
= first:
|
|
second:
|
|
if first != null
|
|
then
|
|
first
|
|
else
|
|
second;
|
|
|
|
mapSubfigure
|
|
= {
|
|
align ? "b",
|
|
body ? null,
|
|
caption ? null,
|
|
cite ? null,
|
|
description ? null,
|
|
file ? null,
|
|
height ? null,
|
|
label ? null,
|
|
plot ? null,
|
|
uncover ? null,
|
|
only ? null,
|
|
width ? null,
|
|
...
|
|
} @ config:
|
|
{
|
|
caption = null;
|
|
description = toDescription ( orDefault description caption);
|
|
inherit align cite label height width uncover only;
|
|
}
|
|
// (
|
|
if file != null
|
|
then
|
|
{
|
|
body = null;
|
|
file
|
|
= type.matchPrimitiveOrPanic file
|
|
{
|
|
path = file;
|
|
string = file;
|
|
};
|
|
options = getOptions' config;
|
|
plot = null;
|
|
}
|
|
else if plot != null
|
|
then
|
|
{
|
|
body = null;
|
|
file = null;
|
|
options = null;
|
|
inherit plot;
|
|
}
|
|
else if body != null
|
|
then
|
|
{
|
|
file = null;
|
|
options = null;
|
|
plot = null;
|
|
inherit body;
|
|
}
|
|
else
|
|
error.unimplemented
|
|
);
|
|
|
|
mapSubfigures
|
|
= list.map
|
|
(
|
|
config:
|
|
if config != null
|
|
then
|
|
mapSubfigure config
|
|
else
|
|
config
|
|
);
|
|
|
|
# { ... } -> Document::Chunk::Figure -> [ string | Indentation ]
|
|
renderFigure
|
|
= _:
|
|
{ label ? null, subfigures ? null, environment, ... } @ figure:
|
|
output:
|
|
let
|
|
render
|
|
= { body, file, options, uncover, only, ... }:
|
|
let
|
|
uncover'
|
|
= text:
|
|
if uncover != null
|
|
then
|
|
[
|
|
"\\uncover<${uncover}>{%" indentation.more
|
|
"\\alt<${uncover}>{%" indentation.more
|
|
"${text}%"
|
|
indentation.less "}{%" indentation.more
|
|
"\\begin{tikzpicture}%" indentation.more
|
|
"\\node[anchor=south west,inner sep=0] (B) at (4,0) {${text}};%"
|
|
"\\fill [draw=none, fill=white, fill opacity=0.9] (B.north west) -- (B.north east) -- (B.south east) -- (B.south west) -- (B.north west) -- cycle;%"
|
|
indentation.less "\\end{tikzpicture}%"
|
|
indentation.less "}%"
|
|
indentation.less "}%"
|
|
]
|
|
else if only != null
|
|
then
|
|
[
|
|
"\\only<${only}>{%" indentation.more
|
|
"${text}%"
|
|
indentation.less "}%"
|
|
]
|
|
else
|
|
[ text ];
|
|
in
|
|
if file != null
|
|
then
|
|
if options != [ ]
|
|
then
|
|
uncover' "\\includegraphics[${string.concatWith "," options}]{\\source/${file}}"
|
|
else
|
|
uncover' "\\includegraphics[width=\\linewidth]{\\source/${file}}"
|
|
else
|
|
body;
|
|
in
|
|
if output == "LaTeX"
|
|
then
|
|
[
|
|
"\\begin{${environment}}[H]%" indentation.more
|
|
"\\centering%"
|
|
]
|
|
++ (
|
|
if subfigures != null
|
|
then
|
|
let
|
|
convert
|
|
= { align, label ? null, width, ... } @ figure:
|
|
[
|
|
"\\begin{subfigure}[${align}]{${width}}" indentation.more
|
|
"\\centering"
|
|
]
|
|
++ ( render figure )
|
|
++ ( putCaption figure )
|
|
++ ( if label != null then [ "\\label{${label}}" ] else [] )
|
|
++ [ indentation.less "\\end{subfigure}" ];
|
|
in
|
|
list.concatMap
|
|
(
|
|
figure:
|
|
(
|
|
if figure != null
|
|
then
|
|
convert figure
|
|
else
|
|
[ "\\hfill" ]
|
|
)
|
|
)
|
|
subfigures
|
|
else
|
|
render figure
|
|
)
|
|
++ ( putCaption figure )
|
|
++ (
|
|
if label != null
|
|
then
|
|
[ "\\labelFigure{${label}}%" ]
|
|
else
|
|
[ ]
|
|
)
|
|
++ [ indentation.less "\\end{${environment}}%" ]
|
|
else if output == "Markdown"
|
|
then
|
|
[]
|
|
else
|
|
debug.panic "render" "Unknown output ${output}";
|
|
|
|
choose
|
|
= list.fold
|
|
(
|
|
result:
|
|
item:
|
|
if result == null
|
|
then
|
|
item
|
|
else
|
|
result
|
|
)
|
|
null;
|
|
in
|
|
{
|
|
# set | string -> list -> list -> Document::Chunk::Figure
|
|
Figure
|
|
= {
|
|
body ? null,
|
|
caption ? null,
|
|
cite ? null,
|
|
dependencies ? [],
|
|
description ? null,
|
|
file ? null,
|
|
height ? null,
|
|
label ? null,
|
|
plot ? null,
|
|
uncover ? null,
|
|
subfigures ? null,
|
|
width ? null,
|
|
only ? null,
|
|
environment ? "figure",
|
|
} @ config:
|
|
chunks.Chunk "Figure"
|
|
{
|
|
render = renderFigure;
|
|
evaluate = evaluateFigure;
|
|
}
|
|
(
|
|
{
|
|
caption = toCaption ( choose [ caption description "" ] );
|
|
description = toDescription ( choose [ description caption "" ] );
|
|
inherit cite label height width uncover only environment;
|
|
}
|
|
// (
|
|
if file != null
|
|
then
|
|
let
|
|
file'
|
|
= type.matchPrimitiveOrPanic file
|
|
{
|
|
path = "resources/figures/${path.getBaseName file}";
|
|
string = file;
|
|
};
|
|
in
|
|
{
|
|
body = null;
|
|
file = file';
|
|
options = getOptions config;
|
|
subfigures = null;
|
|
dependencies
|
|
= dependencies
|
|
++ (
|
|
type.matchPrimitiveOrPanic file
|
|
{
|
|
path = [ { src = "${file}"; dst = "resources/figures/${path.getBaseName file}"; } ];
|
|
string = [];
|
|
}
|
|
);
|
|
}
|
|
else if plot != null
|
|
then
|
|
let
|
|
toPlot
|
|
= data:
|
|
type.matchPrimitiveOrPanic data
|
|
{
|
|
bool = error.throw "Bool in renderFigure?";
|
|
path = "\"${data}\" notitle with lines lt rgb \"0x00777777\"";
|
|
string = "${data} notitle with lines lt rgb \"0x00777777\"";
|
|
set
|
|
= (
|
|
let
|
|
plot
|
|
= if data.file or null != null
|
|
then
|
|
"\"${data.file}\""
|
|
else if data.eq or null != null
|
|
then
|
|
data.eq
|
|
else
|
|
error.unimplemented;
|
|
title
|
|
= if data.title or null != null
|
|
then
|
|
"title \"${data.title}\""
|
|
else
|
|
"notitle";
|
|
lines
|
|
= if data.lines or true
|
|
then
|
|
"with lines lt"
|
|
else
|
|
"";
|
|
colour
|
|
= if data.colour or null != null
|
|
then
|
|
data.colour
|
|
else
|
|
"0x00777777";
|
|
in
|
|
"${plot} ${title} ${lines} rgb \"${colour}\""
|
|
);
|
|
};
|
|
plots
|
|
= if list.isInstanceOf plot.data
|
|
then
|
|
string.concatWith ", " ( list.map toPlot plot.data )
|
|
else
|
|
toPlot plot.data;
|
|
|
|
genPeaks
|
|
= list.map
|
|
(
|
|
{
|
|
x, y,
|
|
z ? 0,
|
|
colour ? "0x00aaaaaa",
|
|
text ? null,
|
|
}:
|
|
let
|
|
x'
|
|
= if set.isInstanceOf x
|
|
then
|
|
( x.from + x.till ) / 2
|
|
else
|
|
x;
|
|
x'' = string (number.round x');
|
|
y' = string y;
|
|
z' = string (number.round ( x' + z ));
|
|
z'' = string (number.round ( x' + z - 60 ));
|
|
in
|
|
if plot.peaksTop or true
|
|
then
|
|
''
|
|
set arrow from first ${x''}, graph 0.8 to first ${x''}, first ${y'} nohead lc rgb "${colour}"
|
|
set arrow from first ${x''}, graph 0.8 to first ${z'}, graph 0.87 nohead lc rgb "${colour}"
|
|
set label "\\tiny ${x''}" right rotate by 90 at first ${z'}, graph 0.88
|
|
${if text != null then ''set label "\\tiny (${text})" right rotate by 90 at first ${z''}, graph 0.88'' else ""}
|
|
''
|
|
else
|
|
''
|
|
set arrow from first ${x''}, graph 0.2 to first ${x''}, first ${y'} nohead lc rgb "${colour}"
|
|
set arrow from first ${x''}, graph 0.2 to first ${z'}, graph 0.13 nohead lc rgb "${colour}"
|
|
set label "\\tiny ${x''}" right rotate by 90 at first ${z'}, graph 0.12
|
|
${if text != null then ''set label "\\tiny (${text})" right rotate by 90 at first ${z''}, graph 0.12'' else ""}
|
|
''
|
|
);
|
|
|
|
formatRange = { min, max }: "[${string min}:${string max}]";
|
|
body
|
|
= string.concatLines
|
|
(
|
|
[]
|
|
++ ( if plot.title or null != null then [ "set title \"{\\\\footnotesize{${plot.title}}}\"" ] else [ "unset title" ] )
|
|
++ ( if plot.xLabel or null != null then [ "set xlabel \"{\\\\small{${plot.xLabel}}}\"" ] else [ "unset xlabel" ] )
|
|
++ ( if plot.yLabel or null != null then [ "set ylabel \"{\\\\small{${plot.yLabel}}}\"" ] else [ "unset ylabel" ] )
|
|
++ ( if plot.xRange or null != null then [ "set xrange ${formatRange plot.xRange}" ] else [] )
|
|
++ ( if plot.yRange or null != null then [ "set yrange ${formatRange plot.yRange}" ] else [] )
|
|
++ ( if plot.xTics or true then [ "set xtics in" ] else [ "unset xtics" ] )
|
|
++ ( if plot.yTics or true then [ "set ytics in" ] else [ "unset ytics" ] )
|
|
++ ( if plot.keyPos or null != null then [ "set key ${plot.keyPos}" ] else [] )
|
|
++ ( if plot.peaks or null != null then genPeaks plot.peaks else [] )
|
|
++ [
|
|
"plot ${plots}"
|
|
"set output"
|
|
]
|
|
);
|
|
hash = string.hash "sha1" body;
|
|
dst = "generated/gnuplot/${hash}-plot";
|
|
epsFile = "${dst}.eps";
|
|
texFile = "${dst}.tex";
|
|
gnuplotSrcFile
|
|
= path.toFile "${hash}.gnuplot"
|
|
''
|
|
set terminal epslatex size ${string width},${string ( if height != null then height else width )}
|
|
set output "${epsFile}"
|
|
${body}
|
|
'';
|
|
gnuplotDstFile = "${dst}.gnuplot";
|
|
|
|
src
|
|
= output:
|
|
string.concatWith " && "
|
|
[
|
|
"(mkdir -p \"generated/gnuplot/\""
|
|
"gnuplot \"${gnuplotSrcFile}\""
|
|
"mv \"${texFile}\" \"${output}.tex\""
|
|
"epstopdf \"${epsFile}\" \"${output}.pdf\")"
|
|
];
|
|
|
|
in
|
|
{
|
|
body = [ "{\\input{\\source/${texFile}}}%" ];
|
|
file = null;
|
|
options = null;
|
|
subfigures = null;
|
|
dependencies
|
|
= dependencies
|
|
++ [
|
|
{ src = gnuplotSrcFile; dst = gnuplotDstFile; }
|
|
{ inherit dst src; }
|
|
];
|
|
}
|
|
else if body != null
|
|
then
|
|
{
|
|
body = list.expect body;
|
|
file = null;
|
|
options = null;
|
|
subfigures = null;
|
|
inherit dependencies;
|
|
}
|
|
else if subfigures != null
|
|
then
|
|
{
|
|
body = null;
|
|
file = null;
|
|
options = null;
|
|
subfigures = mapSubfigures ( list.expect subfigures );
|
|
inherit dependencies;
|
|
}
|
|
else
|
|
error.unimplemented
|
|
)
|
|
);
|
|
} |