From 8591b56496b93eed20f638ee1ce46b11c3f7be58 Mon Sep 17 00:00:00 2001 From: Norbert Preining Date: Sun, 17 Jan 2021 03:02:33 +0000 Subject: CTAN sync 202101170302 --- graphics/pgf/contrib/tikz-bbox/README.txt | 24 ++ .../pgf/contrib/tikz-bbox/pgflibrarybbox.code.tex | 120 ++++++++ .../tikz-bbox/pgfmanual-en-library-bbox.tex | 181 +++++++++++ graphics/pgf/contrib/tikz-bbox/pgfmanual4bbox.pdf | Bin 0 -> 234562 bytes graphics/pgf/contrib/tikz-bbox/pgfmanual4bbox.tex | 26 ++ graphics/pxpic/README.md | 2 +- graphics/pxpic/pxpic.dtx | 342 ++++++++++++++++++--- graphics/pxpic/pxpic.pdf | Bin 295341 -> 309853 bytes 8 files changed, 645 insertions(+), 50 deletions(-) create mode 100644 graphics/pgf/contrib/tikz-bbox/README.txt create mode 100644 graphics/pgf/contrib/tikz-bbox/pgflibrarybbox.code.tex create mode 100644 graphics/pgf/contrib/tikz-bbox/pgfmanual-en-library-bbox.tex create mode 100644 graphics/pgf/contrib/tikz-bbox/pgfmanual4bbox.pdf create mode 100644 graphics/pgf/contrib/tikz-bbox/pgfmanual4bbox.tex (limited to 'graphics') diff --git a/graphics/pgf/contrib/tikz-bbox/README.txt b/graphics/pgf/contrib/tikz-bbox/README.txt new file mode 100644 index 0000000000..077f3aabab --- /dev/null +++ b/graphics/pgf/contrib/tikz-bbox/README.txt @@ -0,0 +1,24 @@ +tikz-bbox +========= + +Copyright 2021 by marmotghost. +v0.1 + +This file may be distributed and/or modified under the +LaTeX project public license (LPPL), version 1.3c +see +https://www.latex-project.org/lppl/lppl-1-3c/ + + +The built-in determination of the bounding box in TikZ is not entirely accurate. +This is because, for Bezier curves, it is the smallest box that contains all +control points, which is in general larger than the box that just contains the +curve. This library determines the exact bounding box of the curve. + +In order to load this library in a LaTeX document, you need to add + +\usetikzlibrary{bbox} + +to the preamble after loading the tikz package. While this library might +possibly work with TeX or ConTeXt, this has never been tested. This library +loads and uses the fpu library. diff --git a/graphics/pgf/contrib/tikz-bbox/pgflibrarybbox.code.tex b/graphics/pgf/contrib/tikz-bbox/pgflibrarybbox.code.tex new file mode 100644 index 0000000000..a9e887f809 --- /dev/null +++ b/graphics/pgf/contrib/tikz-bbox/pgflibrarybbox.code.tex @@ -0,0 +1,120 @@ +% Copyright 2021 by marmotghost +% v0.1 +% +% This file may be distributed and/or modified under the +% LaTeX project public license (LPPL), version 1.3c +% see +% https://www.latex-project.org/lppl/lppl-1-3c/ + + +\usepgflibrary{fpu} +\global\let\pgf@bbox@lt@curveto@normal\pgf@lt@curveto +\global\let\pgf@bbox@nlt@curveto@normal\pgf@nlt@curveto + +\pgfqkeys{/pgf}{bezier bounding box/.is if=pgf@bbox@switch@} + + +\def\pgf@bbox@switch@false{% + \let\pgf@lt@curveto \pgf@bbox@lt@curveto@normal + \let\pgf@nlt@curveto\pgf@bbox@nlt@curveto@normal +} + +\def\pgf@bbox@switch@true{% + \let\pgf@lt@curveto \pgf@bbox@curveto + \let\pgf@nlt@curveto\pgf@bbox@curveto +} +\def\pgf@bbox@curveto#1#2#3#4#5#6{% +\begingroup +\pgfkeys{/pgf/fpu,/pgf/fpu/output format=fixed}% + % extrema in x + % first discriminant d1, must be \ne 0 + \pgfmathsetmacro{\pgf@temp@a}{(\pgf@path@lastx)-(#5)-3*(#1)+3*(#3)}% + \pgfmathtruncatemacro{\pgf@temp@c}{(abs(\pgf@temp@a)>0.1?1:0)}% + \ifnum\pgf@temp@c=1\relax + % second discriminant d2, must be \ge 0 + \pgfmathsetmacro{\pgf@temp@b}{(\pgf@path@lastx)*(#5)-(#5)*(#1)+(#1)*(#1)-(\pgf@path@lastx)*(#3)-(#1)*(#3)+(#3)*(#3)}% + \pgfmathtruncatemacro{\pgf@temp@c}{sign(\pgf@temp@b)}% + \ifnum\pgf@temp@c<0 + \else + \pgfmathsetmacro{\pgf@temp@b}{sqrt(abs(\pgf@temp@b))}% + \pgfmathsetmacro{\pgf@temp@c}{max(0,min(1,((\pgf@path@lastx)-2*(#1)+(#3)-\pgf@temp@b)/\pgf@temp@a))}% + \pgfmathparse{(\pgf@path@lastx)*pow((1-\pgf@temp@c),3)+3*(#1)*pow((1-\pgf@temp@c),2)*\pgf@temp@c+3*(#3)*(1-\pgf@temp@c)*\pgf@temp@c*\pgf@temp@c+(#5)*\pgf@temp@c*\pgf@temp@c*\pgf@temp@c}% + \pgfutil@tempdimb=\pgfmathresult pt\relax% + \pgf@protocolsizes{\pgfutil@tempdimb}{\pgf@path@lasty}% + \pgfmathsetmacro{\pgf@temp@c}{max(0,min(1,((\pgf@path@lastx)-2*(#1)+(#3)+\pgf@temp@b)/\pgf@temp@a))}% + \pgfmathparse{(\pgf@path@lastx)*pow((1-\pgf@temp@c),3)+3*(#1)*pow((1-\pgf@temp@c),2)*\pgf@temp@c+3*(#3)*(1-\pgf@temp@c)*\pgf@temp@c*\pgf@temp@c+(#5)*\pgf@temp@c*\pgf@temp@c*\pgf@temp@c}% + \pgfutil@tempdimb=\pgfmathresult pt\relax% + \pgf@protocolsizes{\pgfutil@tempdimb}{\pgf@path@lasty}% + \fi + \else + % third discriminant d3, must be \ne 0 + \pgfmathsetmacro{\pgf@temp@b}{abs((#5)+(#1)-2*(#3))}% + \pgfmathtruncatemacro{\pgf@temp@c}{(abs(\pgf@temp@b)>0.1?1:0)}% + \ifnum\pgf@temp@c=1\relax + \pgfmathsetmacro{\pgf@temp@c}{((#5)+2*(#1)-3*(#3))/((#5)+(#1)-2*(#3))}% + \pgfmathparse{(\pgf@path@lastx)*pow((1-\pgf@temp@c),3)+3*(#1)*pow((1-\pgf@temp@c),2)*\pgf@temp@c+3*(#3)*(1-\pgf@temp@c)*\pgf@temp@c*\pgf@temp@c+(#5)*\pgf@temp@c*\pgf@temp@c*\pgf@temp@c}% + \pgfutil@tempdimb=\pgfmathresult pt\relax% + \pgf@protocolsizes{\pgfutil@tempdimb}{\pgf@path@lasty}% + \fi + \fi + % 0/0 + \pgfmathsetmacro{\pgf@temp@a}{(#5)+(#1)-2*(#3)}% + \pgfmathtruncatemacro{\pgf@temp@b}{(abs(\pgf@temp@a)>0.1?1:0)}% + \ifnum\pgf@temp@b=1\relax + \pgfmathsetmacro{\pgf@temp@c}{max(0,min(1,((#5)+2*(#1)-3*(#3))/(2*\pgf@temp@a)))}% + \else + \pgfmathsetmacro{\pgf@temp@c}{0.5}% + \fi + \pgfmathparse{(\pgf@path@lastx)*pow((1-\pgf@temp@c),3)+3*(#1)*pow((1-\pgf@temp@c),2)*\pgf@temp@c+3*(#3)*(1-\pgf@temp@c)*\pgf@temp@c*\pgf@temp@c+(#5)*\pgf@temp@c*\pgf@temp@c*\pgf@temp@c}% + \pgfutil@tempdimb=\pgfmathresult pt\relax% + \pgf@protocolsizes{\pgfutil@tempdimb}{\pgf@path@lasty}% +% +% y code + % first discriminant d1, must be \ne 0 + \pgfmathsetmacro{\pgf@temp@a}{(\pgf@path@lasty)-(#6)-3*(#2)+3*(#4)}% + \pgfmathtruncatemacro{\pgf@temp@c}{(abs(\pgf@temp@a)>0.1?1:0)}% + \ifnum\pgf@temp@c=1\relax + % second discriminant d2, must be \ge 0 + \pgfmathsetmacro{\pgf@temp@b}{(\pgf@path@lasty)*(#6)-(#6)*(#2)+(#2)*(#2)-(\pgf@path@lasty)*(#4)-(#2)*(#4)+(#4)*(#4)}% + \pgfmathtruncatemacro{\pgf@temp@c}{sign(\pgf@temp@b)}% + \ifnum\pgf@temp@c<0 + \else + \pgfmathsetmacro{\pgf@temp@b}{sqrt(abs(\pgf@temp@b))}% + \pgfmathsetmacro{\pgf@temp@c}{max(0,min(1,((\pgf@path@lasty)-2*(#2)+(#4)-\pgf@temp@b)/\pgf@temp@a))}% + \pgfmathparse{(\pgf@path@lasty)*pow((1-\pgf@temp@c),3)+3*(#2)*pow((1-\pgf@temp@c),2)*\pgf@temp@c+3*(#4)*(1-\pgf@temp@c)*\pgf@temp@c*\pgf@temp@c+(#6)*\pgf@temp@c*\pgf@temp@c*\pgf@temp@c}% + \pgfutil@tempdimb=\pgfmathresult pt\relax% + \pgf@protocolsizes{\pgf@path@lastx}{\pgfutil@tempdimb}% + \pgfmathsetmacro{\pgf@temp@c}{max(0,min(1,((\pgf@path@lasty)-2*(#2)+(#4)+\pgf@temp@b)/\pgf@temp@a))}% + \pgfmathparse{(\pgf@path@lasty)*pow((1-\pgf@temp@c),3)+3*(#2)*pow((1-\pgf@temp@c),2)*\pgf@temp@c+3*(#4)*(1-\pgf@temp@c)*\pgf@temp@c*\pgf@temp@c+(#6)*\pgf@temp@c*\pgf@temp@c*\pgf@temp@c}% + \pgfutil@tempdimb=\pgfmathresult pt\relax% + \pgf@protocolsizes{\pgf@path@lastx}{\pgfutil@tempdimb}% + \fi + \else + % third discriminant d3, must be \ne 0 + \pgfmathsetmacro{\pgf@temp@b}{abs((#6)+(#2)-2*(#4))}% + \pgfmathtruncatemacro{\pgf@temp@c}{(abs(\pgf@temp@b)>0.1?1:0)}% + \ifnum\pgf@temp@c=1\relax + \pgfmathsetmacro{\pgf@temp@c}{((#6)+2*(#2)-3*(#4))/((#6)+(#2)-2*(#4))}% + \pgfmathparse{(\pgf@path@lasty)*pow((1-\pgf@temp@c),3)+3*(#2)*pow((1-\pgf@temp@c),2)*\pgf@temp@c+3*(#4)*(1-\pgf@temp@c)*\pgf@temp@c*\pgf@temp@c+(#6)*\pgf@temp@c*\pgf@temp@c*\pgf@temp@c}% + \pgfutil@tempdimb=\pgfmathresult pt\relax% + \pgf@protocolsizes{\pgf@path@lastx}{\pgfutil@tempdimb}% + \fi + \fi + % 0/0 + \pgfmathsetmacro{\pgf@temp@a}{(#6)+(#2)-2*(#4)}% + \pgfmathtruncatemacro{\pgf@temp@b}{(abs(\pgf@temp@a)>0.1?1:0)}% + \ifnum\pgf@temp@b=1\relax + \pgfmathsetmacro{\pgf@temp@c}{max(0,min(1,((#6)+2*(#2)-3*(#4))/(2*\pgf@temp@a)))}% + \else + \pgfmathsetmacro{\pgf@temp@c}{0.5}% + \fi + \pgfmathparse{(\pgf@path@lasty)*pow((1-\pgf@temp@c),3)+3*(#2)*pow((1-\pgf@temp@c),2)*\pgf@temp@c+3*(#4)*(1-\pgf@temp@c)*\pgf@temp@c*\pgf@temp@c+(#6)*\pgf@temp@c*\pgf@temp@c*\pgf@temp@c}% + \pgfutil@tempdimb=\pgfmathresult pt\relax% + \pgf@protocolsizes{\pgf@path@lastx}{\pgfutil@tempdimb}% +% + \pgf@protocolsizes{\pgf@path@lastx}{\pgf@path@lasty}% + \pgf@protocolsizes{#5}{#6}% + \endgroup + \pgfsyssoftpath@curveto{\the#1}{\the#2}{\the#3}{\the#4}{\the#5}{\the#6}% +} +\endinput diff --git a/graphics/pgf/contrib/tikz-bbox/pgfmanual-en-library-bbox.tex b/graphics/pgf/contrib/tikz-bbox/pgfmanual-en-library-bbox.tex new file mode 100644 index 0000000000..9d42ae0dd5 --- /dev/null +++ b/graphics/pgf/contrib/tikz-bbox/pgfmanual-en-library-bbox.tex @@ -0,0 +1,181 @@ +% Copyright 2021 by marmotghost +% v0.1 +% +% This file may be distributed and/or modified under the +% LaTeX project public license (LPPL), version 1.3c +% see +% https://www.latex-project.org/lppl/lppl-1-3c/ +% +% It has been claimed that the license of this library had been changed so that this +% library is no longer part of pgf. This claim is a lie. There was no license change +% at all. Now, in 2021, the license got changed to LaTeX project public license +% (LPPL), version 1.3c. + +\section{Bounding Boxes for B\'ezier Curves} + + +\begin{pgflibrary}{bbox} + This library provides methods to determine tight bounding boxes for + B\'ezier curves. This library loads and uses the + |fpu| library. While this library might + possibly work with |TeX| or |ConTeXt|, this has never been tested. +\end{pgflibrary} + +\subsection{Bounding box without the library} + +\tikzname\ determines the bounding box of (cubic) Bezier curves by establishing the +smallest rectangle that contains the end point and the two control points of the +curve. + +\begin{codeexample}[width=5cm] +\begin{tikzpicture}[% + bullet/.style={circle,fill, + inner sep=1pt}] + \draw (0,0) .. controls (-1,1) + and (1,2) .. (2,0); + \draw (current bounding box.south west) + rectangle + (current bounding box.north east); + \draw[red,dashed] + (0,0) -- (-1,1) + node[bullet,label=above:{$(x_a,y_a)$}]{} + (2,0) -- (1,2) + node[bullet,label=above:{$(x_b,y_b)$}]{}; + \path (0,0) + node[bullet,label=below:{$(x_0,y_0)$}]{} + (2,0) + node[bullet,label=below:{$(x_1,y_1)$}]{}; +\end{tikzpicture} +\end{codeexample} + +As one can see from this illustration, this may lead to drastic overestimates of +the bounding box. + +\subsection{Computing the bounding box} + +Establishing the precise bounding box has been discussed in various places, the +following discussion uses in part the results from +\url{https://pomax.github.io/bezierinfo/}. What is a cubic B\'ezier curve? A +cubic B\'ezier curve running from $(x_0,y_0)$ to $(x_1,y_1)$ with control points +$(x_a,y_a)$ and $(x_b,y_b)$ can be parametrized by +\begin{equation} + \gamma(t)~=~ + \begin{pmatrix} x(t)\\ y(t) \end{pmatrix}~=~ + \begin{pmatrix}t^3 x_{1}+3 t^2 (1-t) x_{b}+(1-t)^3 + x_{0}+3 t (1-t)^2 x_{a}\\ + t^3 y_{1}+3 + t^2 (1-t) y_{b}+(1-t)^3 y_{0}+3 t (1-t)^2 + y_{a}\end{pmatrix}\;,\label{eq:gammaBezier} +\end{equation} +where $t$ runs from $0$ to $1$ (and $\gamma(0)=(x_0,y_0)$ and +$\gamma(1)=(x_1,y_1)$). Surely, the bounding box has to contain +$(x_0,y_0)$ and $(x_1,y_1)$. If the functions $x(t)$ and $y(t)$ have extrema in +the interval $[0,1]$, then the bounding box will in general be larger than that. +In order to determine the extrema of the curve, all +we need to find the extrema of the functions $x(t)$ and $y(t)$ for $0\le t\le +1$. That is, we need to find the solutions of the quadratic equations +\begin{equation} + \frac{\mathrm{d}x}{\mathrm{d}t}(t)~=~0\quad\text{and}\quad + \frac{\mathrm{d}y}{\mathrm{d}t}(t)~=~0\;. +\end{equation} +% (*parametrization of x:*) +% myx = x0 (1 - t)^3 + 3 xa (1 - t)^2 t + 3 xb (1 - t) t^2 + x1 t^3 +% (*d1\ne0 condition for t1 and t2 to exist*) +% === (*case d1\ne0*) == +% d1 = x0 - x1 - 3 xa + 3 xb +% (*square root, d2=0 \[Rule] only one solution,d2<0 \[Rule] no solution*) +% d2 = x0*x1 - x1*xa + xa*xa - x0*xb - xa xb + xb*xb +% (*first t*) +% t1 = (x0 - 2*xa + xb - sqrt(d2))/(x0 - x1 - 3*xa + 3*xb) +% = (x0 - 2*xa + xb - sqrt(d2))/d1 +% (*second t*) +% t2 = (x0 - 2*xa + xb + sqrt(d2))/(x0 - x1 - 3*xa + 3*xb) +% = (x0 - 2*xa + xb + sqrt(d2))/d1 +% === (*case d1=0*) == +% (*2nd condition for extra condition: d3\ne0*) +% d3 = x1 + xa - 2 xb +% (*third t*) +% t3 = (x1 + 2*xa - 3*xb)/(2*d3)% d3 = x1 + 3 xa - 3 xb - x0 +Let's discuss $x$ first. If the discriminant +\begin{equation} + d~:=~x_0\,x_1 - x_1\,x_a + x_a\,x_a - x_0\,x_b - x_a x_b + x_b\,x_b +\end{equation} +is greater than 0, there are two solutions +\begin{equation} + t_\pm~=~\frac{x_{0}-2x_{a}+x_{b}\pm\sqrt{d}}{% + x_{0}-x_{1}-3(x_{a}- x_{b})} \;. +\end{equation} +If the denominator $x_{0}-x_{1}-3(x_{a}- x_{b})$ vanishes, one may use the +l'Hospital rule to determine the solutions. +In this case, we need to make sure that the bounding box contains, say +$(x(t_-),y_0)$ and $(x(t_+),y_0)$. If $d\le0$, the bounding box does not need to +be increased in the $x$ direction. On the other hand, if there are solutions, +one needs include the points $\bigl(x(t_\pm),y_0\bigr)$ with $x(t)$ from +\eqref{eq:gammaBezier} in the bounding box. + +The analogous statements apply to $y(t)$. + +\subsection{Using the library} + +\begin{key}{/pgf/bezier bounding box=\meta{boolean} (default true)} + Turn the tight bounding box algorithm on and off. The initial value is + |false|. + + \emph{Caveat:} As can be seen from the derivations, the necessary + computations involve the squaring of lengths and taking ratios of lengths, + which can easily lead to |dimension too large| errors. The library uses + |fpu| to account for that, but errors may still occur. +\end{key} + + +\begin{codeexample}[width=5cm] +\begin{tikzpicture}[bezier bounding box,% + bullet/.style={circle,fill, + inner sep=1pt}] + \draw (0,0) .. controls (-1,1) + and (1,2) .. (2,0); + \draw (current bounding box.south west) + rectangle + (current bounding box.north east); + \draw[red,dashed] + (0,0) -- (-1,1) + node[bullet,label=above:{$(x_a,y_a)$}]{} + (2,0) -- (1,2) + node[bullet,label=above:{$(x_b,y_b)$}]{}; + \path (0,0) + node[bullet,label=below:{$(x_0,y_0)$}]{} + (2,0) + node[bullet,label=below:{$(x_1,y_1)$}]{}; +\end{tikzpicture} +\end{codeexample} + +A few comments are in order. +\begin{enumerate} +\item For paths with arrow heads one may need to load the \texttt{bending} + library. This is because otherwise the quick arrow head distorts the path, and + this happens after the bounding box has been computed. Even worse, arrow heads + could get deformed. +\item If you shorten a path by some negative length, the bounding box will not + be accurate either. However, this has nothing to do with curves, it also + applies to straight lines. So this is not specific to the |bbox| library but + something that one may want to keep in mind. +\item Let us also note that the computations can lead to |Dimension too large| + errors. These errors do not come directly from the computations done by the + library, which uses |fpu| for its computations, but from the aftermath. Many of + these problems can be avoided by using the |fpu| library also for computing + reciprocals, which can be achieved using the key + |/pgf/fpu/install only={reciprocal}|. This allows one to fix many + |Dimension too large| errors in other libraries, which are not related to the + present one, and also fixes inaccuracies of (inverse) transformations when the + scale factors are not integer. +\end{enumerate} + +\subsection{Reporting a bug} + +This library is maintained at the GitHub repository +\href{https://github.com/marmotghost/tikz-bbox}{tikz-bbox}, where issues can get +reported. The author is also active on the noncommercial \TeX\ Q\,\&\,A site +\href{https://topanswers.xyz/tex}{topanswers.xyz/tex}, where requests that are not +issues in the strict sense can be discussed. + +\endinput diff --git a/graphics/pgf/contrib/tikz-bbox/pgfmanual4bbox.pdf b/graphics/pgf/contrib/tikz-bbox/pgfmanual4bbox.pdf new file mode 100644 index 0000000000..69630c5964 Binary files /dev/null and b/graphics/pgf/contrib/tikz-bbox/pgfmanual4bbox.pdf differ diff --git a/graphics/pgf/contrib/tikz-bbox/pgfmanual4bbox.tex b/graphics/pgf/contrib/tikz-bbox/pgfmanual4bbox.tex new file mode 100644 index 0000000000..465bc22bea --- /dev/null +++ b/graphics/pgf/contrib/tikz-bbox/pgfmanual4bbox.tex @@ -0,0 +1,26 @@ +\documentclass[a4paper]{ltxdoc} +\usepackage[version=latest]{pgf} +\usepackage{xkeyval,calc,listings,tikz,fp,amsmath,amssymb} +\usepackage[T1]{fontenc}% +\usepackage{makeidx} +\makeindex +\usepackage{hyperref} +\hypersetup{% + colorlinks=true, + linkcolor=blue, + filecolor=blue, + urlcolor=blue, + citecolor=blue, + pdfborder=0 0 0, +} +\makeatletter % see https://tex.stackexchange.com/q/33946 +\input{pgfmanual.code} % +\makeatother % +\input{pgfmanual-en-macros.tex} % link from +% /usr/local/texlive/2019/texmf-dist/doc/generic/pgf/macros/pgfmanual-en-macros.tex +% or the equivalent on your installation +\def\pgfautoxrefs{1} +\usetikzlibrary{bbox} +\begin{document} +\input{pgfmanual-en-library-bbox.tex} +\end{document} diff --git a/graphics/pxpic/README.md b/graphics/pxpic/README.md index e4e78efbb4..32e7cd516a 100644 --- a/graphics/pxpic/README.md +++ b/graphics/pxpic/README.md @@ -2,7 +2,7 @@ ------------------------------------------------------------------------------- # pxpic -- draw pixel pictures -Version 2021-01-13 v1.1 +Version 2021-01-16 v1.2 Released under the LaTeX Project Public License v1.3c or later See http://www.latex-project.org/lppl.txt diff --git a/graphics/pxpic/pxpic.dtx b/graphics/pxpic/pxpic.dtx index f06454e842..9bb8f81097 100644 --- a/graphics/pxpic/pxpic.dtx +++ b/graphics/pxpic/pxpic.dtx @@ -54,7 +54,7 @@ and the derived files pxpic.pdf \expandafter\endgroup \fi % -\ProvidesFile{pxpic.dtx}[2021-01-13 v1.1 draw pixel pictures] +\ProvidesFile{pxpic.dtx}[2021-01-16 v1.2 draw pixel pictures] \PassOptionsToPackage{full}{textcomp} \documentclass{l3doc} \RequirePackage[oldstylenums,nott]{kpfonts} @@ -78,7 +78,9 @@ and the derived files pxpic.pdf ,basicstyle=\fontfamily{jkp}\itshape ,morekeywords=^^A {^^A - \pxpic,\pxpicsetup + \pxpic,\pxpicsetup,\pxpicnewmode,\pxpicsetmode,\pxpicnewcolorlist, + \pxpicsetcolorlist,\pxpicaddcolorlist,\pxpicforget,\px,\pxskip,\pxpicHT, + \pxpicWD,\pxpiclogo } ,morecomment=[l]\% ,commentstyle=\color[gray]{0.4} @@ -132,20 +134,23 @@ and the derived files pxpic.pdf {expkv}^^A } \hypersetup{linkcolor=red!80!black,urlcolor=purple!80!black} +\DoNotIndex{\advance} \DoNotIndex{\baselineskip,\begingroup,\bgroup} \DoNotIndex{\csname} \DoNotIndex{\def,\detokenize,\dimexpr} -\DoNotIndex{\egroup,\ekvdef,\ekvdefNoVal,\ekvifdefinedNoVal,\ekvletkv,\ekvparse} -\DoNotIndex{\ekvsetdef,\endcsname,\endgroup,\expandafter} +\DoNotIndex{\edef,\egroup,\ekvdef,\ekvdefNoVal,\ekvifdefinedNoVal,\ekvletkv} +\DoNotIndex{\ekvparse,\ekvsetdef,\endcsname,\endgroup,\expandafter} \DoNotIndex{\hbox,\hskip} +\DoNotIndex{\iffalse,\fi} \DoNotIndex{\kern} \DoNotIndex{\leavevmode,\let,\long,\lower} \DoNotIndex{\newcommand,\newdimen} \DoNotIndex{\PackageError,\protected,\ProvidesPackage} \DoNotIndex{\relax,\RequirePackage} -\DoNotIndex{\@depth,\@height,\@ifdefinable,\@ifnextchar,\@ifundefined,\@width} +\DoNotIndex{\@declaredcolor,\@firstofone,\@gobble,\@ifdefinable,\@ifnextchar} +\DoNotIndex{\@ifundefined,\@undeclaredcolor} \DoNotIndex{\ekv@ifempty,\ekv@name,\z@} -\DoNotIndex{\color} +\DoNotIndex{\unexpanded} \DoNotIndex{\vbox,\vrule} \@ifdefinable\gobbledocstriptag{\def\gobbledocstriptag#1>{}} \makeatother @@ -207,7 +212,7 @@ and the derived files pxpic.pdf \Large draw pixel pictures^^A }{pxpic - draw pixel pictures}^^A } - \date{2021-01-13 v1.1} + \date{2021-01-16 v1.2} \author{Jonathan P. Spratte\thanks{\protect\randomize{jspratte@yahoo.de}}} \DocInput{pxpic.dtx} \end{document} @@ -570,24 +575,37 @@ and the derived files pxpic.pdf % The available options are % % \begin{options} -% \item[colors] Define pixel colours for |mode=px|, see \autoref{sec:colours} -% for a description of the value's syntax. No pixel definitions are made by -% the package. -% \item[colours] \emph{see \texttt{colors}.} -% \item[gap-hack] To fix the issues with visible gaps in \textsc{pdf} viewers -% you can introduce some negative kerns to make the pixels overlap (lines -% overlap to the top, pixels to the left). This option expects a dimension -% as its value. A positive value will (maybe) close the gaps, a negative -% value will introduce real gaps. In any case the outermost pixels' borders -% still coincide with the borders of the surrounding \CS{hbox}. Take a look -% at my babbling about this issue in \autoref{sec:misc}. -% \item[ht] Set the height of the pixels. -% \item[mode] Set the used mode, see \autoref{sec:modes} for available modes. -% Initial value is |px|. -% \item[size] Set both |ht| and |wd|. Initial value is \the\pxpicHT. -% \item[skip] Define the value to be a skip (an empty space of width |wd|) in -% |mode=px|. No skip definitions are made by the package. -% \item[wd] Set the width of the pixels. +% \item[colors=\meta{colour list}] +% Define pixel colours for |mode=px|, see \autoref{sec:colours} for a +% description of the value's syntax. No pixel definitions are made by the +% package. +% \item[colours] +% \emph{see \texttt{colors}.} +% \item[color-list=\meta{choice}] +% loads a previously through \CS{pxpicnewcolorlist} defined colour list. No +% colour lists are defined by the package. +% \item[colour-list] +% \emph{see \texttt{color-list}.} +% \item[gap-hack=\meta{dimen}] +% To fix the issues with visible gaps in \textsc{pdf} viewers you can +% introduce some negative kerns to make the pixels overlap (lines overlap to +% the top, pixels to the left). This option expects a dimension as its +% value. A positive value will (maybe) close the gaps, a negative value will +% introduce real gaps. In any case the outermost pixels' borders still +% coincide with the borders of the surrounding \CS{hbox}. Take a look at my +% babbling about this issue in \autoref{sec:misc}. +% \item[ht=\meta{dimen}] +% Set the height of the pixels. +% \item[mode=\meta{choice}] +% Set the used mode, see \autoref{sec:modes} for available modes. Initial +% value is |px|. +% \item[size=\meta{dimen}] +% Set both |ht| and |wd|. Initial value is \the\pxpicHT. +% \item[skip=\meta{dimen}] +% Define the value to be a skip (an empty space of width |wd|) in |mode=px|. +% No skip definitions are made by the package. +% \item[wd=\meta{dimen}] +% Set the width of the pixels. % \end{options} % % \subsubsection{Colour syntax}\label{sec:colours} @@ -649,6 +667,28 @@ and the derived files pxpic.pdf % already exists, \cs{pxpicsetmode} has no checks on the name. % \end{function} % +% \begin{function}{\pxpicnewcolorlist,\pxpicsetcolorlist,\pxpicaddcolorlist} +% \begin{syntax} +% \cs{pxpicnewcolorlist}\marg{name}\marg{colour list} +% \end{syntax} +% This defines a colour list (to be used with the |colour-list| option). The +% syntax of \meta{colour list} is the same as for the |colours| option. The +% pixels aren't directly defined, but only by the use of +% |colour-list=|\meta{name}. So +% \begin{lstlisting} +% \pxpicnewcolorlist{example}{r=red,b=blue,g=green,k=black,w=white} +% \pxpicsetup{colour-list=example} +% \end{lstlisting} +% would have the same effect as +% \begin{lstlisting} +% \pxpicsetup{colours={r=red,b=blue,g=green,k=black,w=white}} +% \end{lstlisting} +% but a |colour-list| is more efficient if used multiple times. The |new| +% variant will only throw an error if the colour list \meta{name} is already +% defined. The |set| variant has no such tests, and the |add| variant will add +% additional colours to an existing list. +% \end{function} +% % \begin{function}{\pxpicforget} % \begin{syntax} % \cs{pxpicforget}\marg{px} @@ -695,14 +735,77 @@ and the derived files pxpic.pdf % \textsc{pdf} viewers and tools will display such a gap. To make things even % worse, the effect depends on the viewers current magnification. % \pxpicname\ has the |gap-hack| option to provide some crude hack that might -% fix the issue, at the cost that the pixels are smaller than they were -% specified to be, except for the pixels at the right and bottom border. Also -% pixels next to skipped pixels have a different size (skipped pixels don't -% cover pixels to their left or top as they are transparent). You'll want to -% find a good trade-off value if you want to use |gap-hack|, that mitigates the -% effect but isn't too big (to make the errors less obvious). You can play with -% the value and decide for yourself what's the lesser evil. Or you do like me, -% don't use |gap-hack| and blame the viewers. +% fix the issue, at the cost that the pixels on the far right and bottom are +% bigger than they were specified to be. Also pixels next to skipped pixels have +% a different size (skipped pixels don't cover pixels to their left or top as +% they are transparent). You'll want to find a good trade-off value if you want +% to use |gap-hack|, that mitigates the effect but isn't too big (to make the +% errors less obvious). You can play with the value and decide for yourself +% what's the lesser evil. Or you do like me, don't use |gap-hack| and blame the +% viewers. Here are examples in which you can compare (the |gap-hack| is chosen +% way too big in this example and skips are used close to white pixels on +% purpose, but it illustrates the effects; the third output, not shown in the +% code, uses a more reasonable |gap-hack=.2pt|):\par\nobreak +% \noindent +% \begin{minipage}[c]{.5\linewidth}\footnotesize +% \begin{lstlisting} +% \pxpicsetup +% { +% colours={k=black,g=green,w=white} +% ,skip=. +% ,size=10pt +% } +% \pxpic[gap-hack=2pt] +% { +% {kkkkk} +% {kgggk} +% {kwg.k} +% {kg.gk} +% {kgwk} +% {kkkw} +% } +% \raise2pt\pxpic +% { +% {kkkkk} +% {kgggk} +% {kwg.k} +% {kg.gk} +% {kgwk} +% {kkkw} +% } +% \end{lstlisting} +% \end{minipage} +% \begin{minipage}[c]{.45\linewidth} +% \pxpicsetup{colours={k=black,g=green,w=white},skip=.,size=10pt} +% \leavevmode +% \pxpic[gap-hack=2pt] +% { +% {kkkkk} +% {kgggk} +% {kwg.k} +% {kg.gk} +% {kgwk} +% {kkkw} +% } +% \raise2pt\pxpic +% { +% {kkkkk} +% {kgggk} +% {kwg.k} +% {kg.gk} +% {kgwk} +% {kkkw} +% } +% \raise1.8pt\pxpic[gap-hack=.2pt] +% { +% {kkkkk} +% {kgggk} +% {kwg.k} +% {kg.gk} +% {kgwk} +% {kkkw} +% } +% \end{minipage} % % % \end{documentation}^^A=<< @@ -718,7 +821,7 @@ and the derived files pxpic.pdf % % Report who we are % \begin{macrocode} -\ProvidesPackage{pxpic}[2021-01-13 v1.1 draw pixel pictures] +\ProvidesPackage{pxpic}[2021-01-16 v1.2 draw pixel pictures] % \end{macrocode} % and load dependencies % \begin{macrocode} @@ -764,7 +867,7 @@ and the derived files pxpic.pdf % \end{macrocode} % The |colours| option is parsed using \CS{ekvparse} and \CS{pxpic@setcolor}. % \begin{macrocode} -\protected\ekvdef{pxpic}{colors}{\ekvparse\pxpic@noval\pxpic@setcolor{#1}} +\protected\ekvdef{pxpic}{colors}{\ekvparse\pxpic@err@noval\pxpic@setcolor{#1}} \ekvletkv{pxpic}{colours}{pxpic}{colors} % \end{macrocode} % And the |mode| just checks whether the |mode| macro is defined and lets the @@ -773,13 +876,23 @@ and the derived files pxpic.pdf \protected\ekvdef{pxpic}{mode} {% \@ifundefined{pxpic@parse@px@#1}% - {\pxpic@unknown@mode{#1}}% + {\pxpic@err@unknown@mode{#1}}% {% \expandafter\let\expandafter\pxpic@parse@px \csname pxpic@parse@px@#1\endcsname }% } % \end{macrocode} +% A similar check is done for the |colour-list| option. +% \begin{macrocode} +\protected\ekvdef{pxpic}{color-list} + {% + \@ifundefined{pxpic@colorlist@#1}% + {\pxpic@err@unknown@colorlist{#1}} + {\csname pxpic@colorlist@#1\endcsname}% + } +\ekvletkv{pxpic}{colour-list}{pxpic}{color-list} +% \end{macrocode} % % % \subsection{User macros}\label{sec:implementation:pxpiclogo} @@ -795,7 +908,7 @@ and the derived files pxpic.pdf % pixel row will be wrapped inside an \CS{hbox}. The \CS{kern} negates a % negative \CS{kern} in \CS{pxpic@parse} so that the first line isn't moved. % \begin{macrocode} -\newcommand*\pxpic{\hbox\bgroup\pxpic@} +\@ifdefinable\pxpic{\protected\def\pxpic{\hbox\bgroup\pxpic@}} \newcommand\pxpic@[2][] {% \vbox @@ -803,6 +916,8 @@ and the derived files pxpic.pdf \pxpicsetup{#1}% \let\px\pxpic@px \let\pxskip\pxpic@skip + \advance\pxpicHT\pxpic@kern + \advance\pxpicWD\pxpic@kern \baselineskip\pxpicHT \kern\pxpic@kern \pxpic@parse#2\pxpic@end @@ -832,6 +947,7 @@ and the derived files pxpic.pdf \begingroup \pxpicHT\dimexpr#1\relax \pxpicWD\pxpicHT + \pxpic@kern\z@ \leavevmode \lower3.2\pxpicHT\pxpic [mode=px,colours={o=[HTML]{9F393D},g=black!75},skip=.] @@ -869,13 +985,64 @@ and the derived files pxpic.pdf % These are pretty simple as well, the |new| variant will use \CS{newcommand} % which will do the testing for us, the |set| variant uses \CS{def}. % \begin{macrocode} -\protected\def\pxpicnewmode#1#2% +\protected\long\def\pxpicnewmode#1#2% {\expandafter\newcommand\csname pxpic@parse@px@#1\endcsname[1]{#2}} -\protected\def\pxpicsetmode#1#2% +\protected\long\def\pxpicsetmode#1#2% {\long\expandafter\def\csname pxpic@parse@px@#1\endcsname##1{#2}} % \end{macrocode} % \end{macro} % +% \begin{macro}{\pxpicnewcolorlist,\pxpicsetcolorlist,\pxpicaddcolorlist} +% \begin{macro}[internal]{\pxpicsetcolorlist@,\pxpicaddcolorlist@} +% The colour list is first parsed with \CS{ekvparse} inside an \CS{edef}. +% \CS{ekvparse} will prevent the parsed list from further expanding, leaving +% each list element and \CS{pxpic@experr@noval} or +% \CS{pxpic@setcolor@colorlist} before it. In a second \CS{edef} these will be +% expanded, \CS{pxpic@experr@noval} throwing an error for each element missing +% a colour definition, and \CS{pxpic@setcolor@colorlist} testing for an +% opening bracket (which we do expandably) and leaving the correct definition +% protected against further expansion. The |add| variant uses a temporary +% macro for the parsing part and adds the result to the list holding macro. +% The second expansion step in |set| and both in |add| are done inside a group +% to revert any definition (also those letting tokens to \CS{relax} by +% \CS{csname}) made at this point except for the list macro itself. +% \begin{macrocode} +\protected\def\pxpicnewcolorlist#1% + {% + \@ifundefined{pxpic@colorlist@#1} + {\pxpicsetcolorlist{#1}} + {\pxpic@err@defined@colorlist{#1}\@gobble}% + } +\protected\def\pxpicsetcolorlist#1% + {\expandafter\pxpicsetcolorlist@\csname pxpic@colorlist@#1\endcsname} +\protected\long\def\pxpicsetcolorlist@#1#2% + {% + \edef#1{\ekvparse\pxpic@experr@noval\pxpic@setcolor@colorlist{#2}}% + \begingroup\edef#1{\endgroup\protected\def\unexpanded{#1}{#1}}% + #1% + } +\protected\def\pxpicaddcolorlist#1% + {% + \@ifundefined{pxpic@colorlist@#1} + {\pxpic@err@unknown@colorlist{#1}\@gobble} + {\expandafter\pxpicaddcolorlist@\csname pxpic@colorlist@#1\endcsname}% + } +\protected\long\def\pxpicaddcolorlist@#1#2% + {% + \begingroup + \edef\pxpic@tmp + {\ekvparse\pxpic@experr@noval\pxpic@setcolor@colorlist{#2}}% + \edef\pxpic@tmp + {% + \endgroup + \protected\def\unexpanded{#1}{\unexpanded\expandafter{#1}\pxpic@tmp}% + }% + \pxpic@tmp + } +% \end{macrocode} +% \end{macro} +% \end{macro} +% % % \subsection{Parser} % @@ -892,6 +1059,15 @@ and the derived files pxpic.pdf % \end{macrocode} % \end{macro} % +% \begin{macro}[internal]{\pxpic@openbrace} +% For some weirder \TeX\ programming it is sometimes necessary to insert an +% unmatched opening brace. This code does exactly that if it's expanded twice. +% It is put into a single macro so that one can \CS{expandafter} it easier. +% \begin{macrocode} +\newcommand*\pxpic@openbrace{\expandafter{\iffalse}\fi} +% \end{macrocode} +% \end{macro} +% % \begin{macro}[internal]{\pxpic@parse,\pxpic@done} % The parsing loop is pretty simple, first check whether we're done, else open % a new \CS{hbox} (which will form a row in the \CS{vbox} placed by @@ -949,7 +1125,7 @@ and the derived files pxpic.pdf \ekvifdefinedNoVal{pxpic@px}{#1} {\csname\ekv@name{pxpic@px}{#1}N\endcsname}% {% - \pxpic@unknown@px{#1}% + \pxpic@err@unknown@px{#1}% \pxskip }% } @@ -965,7 +1141,7 @@ and the derived files pxpic.pdf {% \pxpic@ifempty{#1} {\pxskip} - {{\color{#1}\px}}% + {{\@declaredcolor{#1}\px}}% } % \end{macrocode} % \end{macro} @@ -996,7 +1172,7 @@ and the derived files pxpic.pdf {% \pxpic@ifempty{##1} {\pxskip} - {{\color[#1]{##1}\px}}% + {{\@undeclaredcolor[#1]{##1}\px}}% }% } \pxpic@tmp{rgb} @@ -1022,7 +1198,7 @@ and the derived files pxpic.pdf % The actual definition of pixels and skips is stored in macros to which the % frontend macros \CS{px} and \CS{pxskip} will be let inside of \CS{pxpic}. % \begin{macrocode} -\newcommand\pxpic@px{\vrule\@height\pxpicHT\@width\pxpicWD\@depth\z@} +\newcommand\pxpic@px{\vrule height\pxpicHT width\pxpicWD depth\z@} \newcommand\pxpic@skip{\hskip\pxpicWD} % \end{macrocode} % \end{macro} @@ -1046,24 +1222,92 @@ and the derived files pxpic.pdf {#1}{#2}% } \newcommand\pxpic@setcolor@a[2] - {\ekvdefNoVal{pxpic@px}{#1}{{\color{#2}\px}}} + {% + \expandafter\def\csname\ekv@name{pxpic@px}{#1}N\endcsname + {{\@declaredcolor{#2}\px}}% + } \newcommand\pxpic@setcolor@b[2] - {\ekvdefNoVal{pxpic@px}{#1}{{\color#2\px}}} + {% + \expandafter\def\csname\ekv@name{pxpic@px}{#1}N\endcsname + {{\@undeclaredcolor#2\px}}% +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[internal]{\pxpic@setcolor@colorlist} +% This macro should leave the correct code in the input stream to define a +% single pixel. It is to be used inside of \CS{edef}, hence using +% \CS{unexpanded}, which doesn't have an opening brace directly after it +% so that the \CS{pxpic@ifbracket} test is fully expanded. Next we expand +% \CS{pxpic@setcolor@a}/|b| twice (which will expand the \CS{csname} +% contained in it) and then leave the opening bracket for \CS{unexpanded} in +% the input stream. The code should be used inside a group so that all the +% implicit definitions to \CS{relax} done by \CS{csname} are reverted. +% \begin{macrocode} +\newcommand\pxpic@setcolor@colorlist[2] + {% + \unexpanded\iffalse{\fi + \pxpic@ifbracket\pxpic@end#2.\pxpic@end[]\pxpic@end + {\expandafter\expandafter\expandafter\pxpic@openbrace\pxpic@setcolor@a} + {\expandafter\expandafter\expandafter\pxpic@openbrace\pxpic@setcolor@b} + {#1}{#2}% + }% + } % \end{macrocode} % \end{macro} % % % \subsection{Messages} % -% \begin{macro}[internal]{\pxpic@noval,\pxpic@unknown@px,\pxpic@unknown@mode} +% \begin{macro}[internal] +% { +% \pxpic@err@noval,\pxpic@err@unknown@px,\pxpic@err@unknown@mode, +% \pxpic@err@unknown@colorlist,\pxpic@err@defined@colorlist +% } % These are just some macros throwing errors, nothing special here. % \begin{macrocode} -\newcommand\pxpic@noval[1] +\newcommand\pxpic@err@noval[1] {\PackageError{pxpic}{Missing colour definition for name `\detokenize{#1}'}{}} -\newcommand\pxpic@unknown@px[1] +\newcommand\pxpic@err@unknown@px[1] {\PackageError{pxpic}{Unknown pixel `\detokenize{#1}'. Skipping}{}} -\newcommand\pxpic@unknown@mode[1] +\newcommand\pxpic@err@unknown@mode[1] {\PackageError{pxpic}{Unknown mode `#1'}{}} +\newcommand\pxpic@err@unknown@colorlist[1] + {\PackageError{pxpic}{Unknown colour list `#1'}{}} +\newcommand\pxpic@err@defined@colorlist[1] + {\PackageError{pxpic}{Colour list `#1' already defined}{}} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[internal]{\pxpic@experr} +% This macro can be used to throw an error expandably. For this an undefined +% control sequence \verb*|\pxpic Error:| is used. The group containing +% \CS{expandafter} keeps the definition of \verb*|\pxpic Error:| local (it is +% \CS{relax} after the \CS{csname}) so that it is undefined when it's used. +% The \CS{@firstofone} is needed to get the readable output (now the undefined +% macro and actual message are always the same argument). +% \begin{macrocode} +\def\pxpic@experr#1% + {% + \long\def\pxpic@experr##1% + {% + \expandafter\expandafter\expandafter + \pxpic@ifend + \@firstofone{#1##1}% + \pxpic@end + }% + } +\begingroup\expandafter\endgroup +\expandafter\pxpic@experr\csname pxpic Error:\endcsname +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[internal]{\pxpic@experr@noval} +% With the expandable error throwing mechanism out of the way, the following +% is straight forward again. +% \begin{macrocode} +\newcommand\pxpic@experr@noval[1] + {\pxpic@experr{Missing colour definition for `#1'}} % \end{macrocode} % \end{macro} % diff --git a/graphics/pxpic/pxpic.pdf b/graphics/pxpic/pxpic.pdf index 2148303a47..075ce07723 100644 Binary files a/graphics/pxpic/pxpic.pdf and b/graphics/pxpic/pxpic.pdf differ -- cgit v1.2.3