|
| 1 | +% pgfsubpic.tex |
| 2 | +% Version 1.1, 25 Dec 2009 |
| 3 | + |
| 4 | +% Copyright 2009 by David Chiang |
| 5 | + |
| 6 | +% This program is free software; you can redistribute it and/or modify |
| 7 | +% it under the terms of the GNU General Public License as published by |
| 8 | +% the Free Software Foundation; either version 2 of the License, or |
| 9 | +% (at your option) any later version. |
| 10 | + |
| 11 | +% This program is distributed in the hope that it will be useful, |
| 12 | +% but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 13 | +% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 14 | +% GNU General Public License for more details. |
| 15 | + |
| 16 | +% You should have received a copy of the GNU General Public License along |
| 17 | +% with this program; if not, write to the Free Software Foundation, Inc., |
| 18 | +% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. |
| 19 | + |
| 20 | +% New in version 1.1: |
| 21 | +% - the ability to save a subpicture in local variables |
| 22 | +% - nodes in subpictures are tracked if the subpicture is placed with arbitrary transforms |
| 23 | +% - new \pgffitsubpicture macro to transform a subpicture (preserving aspect) to fit in a desired box |
| 24 | + |
| 25 | +\newdimen\pgf@subpicminx |
| 26 | +\newdimen\pgf@subpicminy |
| 27 | +\newdimen\pgf@subpicmaxx |
| 28 | +\newdimen\pgf@subpicmaxy |
| 29 | + |
| 30 | +% Special virtual node for current subpicture's bounding box |
| 31 | +\expandafter\def\csname pgf@sh@ns@current subpicture\endcsname{rectangle} |
| 32 | +\expandafter\def\csname pgf@sh@np@current subpicture\endcsname{% |
| 33 | + \def\southwest{\pgfqpoint{\pgf@subpicminx}{\pgf@subpicminy}}% |
| 34 | + \def\northeast{\pgfqpoint{\pgf@subpicmaxx}{\pgf@subpicmaxy}}% |
| 35 | +} |
| 36 | +\expandafter\def\csname pgf@sh@nt@current subpicture\endcsname{{\pgf@pt@aa}{\pgf@pt@ab}{\pgf@pt@ba}{\pgf@pt@bb}{\the\pgf@pt@x}{\the\pgf@pt@y}} % the transformation at invocation time |
| 37 | +\expandafter\def\csname pgf@sh@pi@current subpicture\endcsname{\pgfpictureid} |
| 38 | + |
| 39 | +% Create a pgfpicture inside an hbox for delayed placement |
| 40 | +\def\pgfsubpicture{% |
| 41 | +\expandafter\global\expandafter\setbox\pgf@hbox=\hbox\bgroup |
| 42 | +\pgfinterruptpicture |
| 43 | +\pgfpicture |
| 44 | +\relax % not sure why. otherwise a curly brace immediately after causes an error |
| 45 | +} |
| 46 | + |
| 47 | +\def\endpgfsubpicture{ |
| 48 | +\global\pgf@subpicminx=\pgf@picminx |
| 49 | +\global\pgf@subpicminy=\pgf@picminy |
| 50 | +\global\pgf@subpicmaxx=\pgf@picmaxx |
| 51 | +\global\pgf@subpicmaxy=\pgf@picmaxy |
| 52 | +\global\edef\subpictureid{\pgfpictureid}% |
| 53 | +\pgfsetbaseline{\pgf@picminy}% |
| 54 | +\endpgfpicture% |
| 55 | +\endpgfinterruptpicture% |
| 56 | +\egroup |
| 57 | +} |
| 58 | + |
| 59 | +% Allocate registers for saving a subpicture. #1 is text, not a control sequence. |
| 60 | +\def\pgfnewsubpicture#1{% |
| 61 | +\expandafter\newbox\csname pgf@subpic@hbox@#1\endcsname |
| 62 | +\expandafter\newdimen\csname pgf@subpic@minx@#1\endcsname |
| 63 | +\expandafter\newdimen\csname pgf@subpic@miny@#1\endcsname |
| 64 | +\expandafter\newdimen\csname pgf@subpic@maxx@#1\endcsname |
| 65 | +\expandafter\newdimen\csname pgf@subpic@maxy@#1\endcsname |
| 66 | +} |
| 67 | + |
| 68 | +% saved subpictures are local to the current group |
| 69 | +\def\pgfsavesubpicture#1{% |
| 70 | +\expandafter\setbox\csname pgf@subpic@hbox@#1\endcsname\box\pgf@hbox |
| 71 | +\csname pgf@subpic@minx@#1\endcsname\pgf@subpicminx |
| 72 | +\csname pgf@subpic@miny@#1\endcsname\pgf@subpicminy |
| 73 | +\csname pgf@subpic@maxx@#1\endcsname\pgf@subpicmaxx |
| 74 | +\csname pgf@subpic@maxy@#1\endcsname\pgf@subpicmaxy |
| 75 | +\expandafter\edef\csname pgf@subpic@id@#1\endcsname{\subpictureid}% |
| 76 | +} |
| 77 | + |
| 78 | +% place current subpicture into named subpicture |
| 79 | +\def\pgfmergesubpicture#1{% |
| 80 | +\begin{pgfsubpicture} |
| 81 | +% place current subpicture |
| 82 | +\pgfplacesubpicture |
| 83 | +% override containing picture |
| 84 | +\expandafter\xdef\csname pgf@sh@pi@\subpictureid\endcsname{\csname pgf@subpic@id@#1\endcsname}% |
| 85 | +% copy contents of #1 |
| 86 | +\pgfrestoresubpicture{#1} |
| 87 | +\pgflowlevelobj{\pgftransformshift{\pgfqpoint{\the\pgf@subpicminx}{\the\pgf@subpicminy}}}{\pgfqbox\pgf@hbox} |
| 88 | +\pgfpathrectanglecorners{\pgfqpoint{\the\pgf@subpicminx}{\the\pgf@subpicminy}}{\pgfqpoint{\the\pgf@subpicmaxx}{\the\pgf@subpicmaxy}}% |
| 89 | +\pgfusepath{use as bounding box}% |
| 90 | +% |
| 91 | +\end{pgfsubpicture} |
| 92 | +\expandafter\setbox\csname pgf@subpic@hbox@#1\endcsname\box\pgf@hbox |
| 93 | +\csname pgf@subpic@minx@#1\endcsname\pgf@subpicminx |
| 94 | +\csname pgf@subpic@miny@#1\endcsname\pgf@subpicminy |
| 95 | +\csname pgf@subpic@maxx@#1\endcsname\pgf@subpicmaxx |
| 96 | +\csname pgf@subpic@maxy@#1\endcsname\pgf@subpicmaxy |
| 97 | +% but don't save the new picture id, keep the existing one |
| 98 | +} |
| 99 | + |
| 100 | +\def\pgfrestoresubpicture#1{% |
| 101 | +\edef\act{\global\noexpand\setbox\pgf@hbox\noexpand\box\csname pgf@subpic@hbox@#1\endcsname}\act |
| 102 | +\expandafter\global\expandafter\pgf@subpicminx\csname pgf@subpic@minx@#1\endcsname |
| 103 | +\expandafter\global\expandafter\pgf@subpicminy\csname pgf@subpic@miny@#1\endcsname |
| 104 | +\expandafter\global\expandafter\pgf@subpicmaxx\csname pgf@subpic@maxx@#1\endcsname |
| 105 | +\expandafter\global\expandafter\pgf@subpicmaxy\csname pgf@subpic@maxy@#1\endcsname |
| 106 | +\xdef\subpictureid{\csname pgf@subpic@id@#1\endcsname}% |
| 107 | +} |
| 108 | + |
| 109 | +% Place a previously-created subpicture, lining up its origin with the current origin |
| 110 | +\def\pgfplacesubpicture{ |
| 111 | +\pgfscope |
| 112 | +% expand current bounding box to accommodate subpicture |
| 113 | +\pgfpathrectanglecorners{\pgfqpoint{\the\pgf@subpicminx}{\the\pgf@subpicminy}}{\pgfqpoint{\the\pgf@subpicmaxx}{\the\pgf@subpicmaxy}}% |
| 114 | +\pgfusepath{use as bounding box}% |
| 115 | +% |
| 116 | +% make the subpicture a node in the containing picture |
| 117 | +\expandafter\gdef\csname pgf@sh@ns@\subpictureid\endcsname{rectangle}% |
| 118 | +\expandafter\xdef\csname pgf@sh@np@\subpictureid\endcsname{% |
| 119 | + \noexpand\def\noexpand\southwest{\noexpand\pgfqpoint{\the\pgf@subpicminx}{\the\pgf@subpicminy}}% |
| 120 | + \noexpand\def\noexpand\northeast{\noexpand\pgfqpoint{\the\pgf@subpicmaxx}{\the\pgf@subpicmaxy}}% |
| 121 | +}% |
| 122 | +\pgfgettransform\pgf@temp |
| 123 | +\expandafter\xdef\csname pgf@sh@nt@\subpictureid\endcsname{\pgf@temp}% |
| 124 | +\expandafter\xdef\csname pgf@sh@pi@\subpictureid\endcsname{\pgfpictureid}% |
| 125 | +% |
| 126 | +% align origin of subpicture with origin |
| 127 | +\pgftransformshift{\pgfqpoint{\the\pgf@subpicminx}{\the\pgf@subpicminy}}% |
| 128 | +\pgfqboxsynced{\pgf@hbox}% |
| 129 | +\endpgfscope |
| 130 | +} |
| 131 | + |
| 132 | +% Hook onto existing macro \pgf@shape@interpictureshift. |
| 133 | +% This is called whenever we look up an anchor of a node. |
| 134 | +% This hook recursively checks to see if the node's picture |
| 135 | +% is a subpicture of another, and if so, adjusts its position accordingly. |
| 136 | + |
| 137 | +% This is slow. It makes drawing trees O(n^2) in the depth of the tree. |
| 138 | +% The alternative is to store, for each picture, a list of the nodes |
| 139 | +% inside it. But this way doesn't require us to hijack \pgfnode, and |
| 140 | +% is robust to re-placement of a subpicture. A compromise would be |
| 141 | +% to store, for each picture, a list of the *subpictures* inside it. |
| 142 | + |
| 143 | +\let\orig@pgf@shape@interpictureshift\pgf@shape@interpictureshift |
| 144 | +\def\unwind@subpic#1{% |
| 145 | +% is #1 the current picture? |
| 146 | +\edef\subpicid{#1}% |
| 147 | +\ifx\subpicid\pgfpictureid |
| 148 | +% yes, we're done |
| 149 | +\else |
| 150 | +% does #1 have a parent picture? |
| 151 | +\expandafter\ifx\csname pgf@sh@pi@#1\endcsname\relax |
| 152 | +% no, the original node was not inside the current picture |
| 153 | +\fallback |
| 154 | +\else |
| 155 | +% yes, apply transform and move up to parent picture |
| 156 | +{% |
| 157 | + \pgfsettransform{\csname pgf@sh@nt@#1\endcsname}% |
| 158 | + \pgf@pos@transform{\pgf@x}{\pgf@y}% |
| 159 | + \global\pgf@x=\pgf@x |
| 160 | + \global\pgf@y=\pgf@y |
| 161 | +}% |
| 162 | +\unwind@subpic{\csname pgf@sh@pi@#1\endcsname}% |
| 163 | +\fi |
| 164 | +\fi |
| 165 | +} |
| 166 | +\def\pgf@shape@interpictureshift#1{% |
| 167 | +\edef\fallback{\pgf@x=\the\pgf@x\pgf@y=\the\pgf@y\noexpand\orig@pgf@shape@interpictureshift{#1}}% |
| 168 | +\unwind@subpic{\csname pgf@sh@pi@#1\endcsname}% |
| 169 | +} |
| 170 | + |
| 171 | +% \pgffitsubpicture{sw}{ne} |
| 172 | +% Make the subpicture fit in the rectangle from sw to ne, preserving its aspect ratio. |
| 173 | +\def\pgffitsubpicture#1#2{% |
| 174 | +% current size |
| 175 | +\pgfpointdiff{\pgfpointanchor{current subpicture}{south west}}{\pgfpointanchor{current subpicture}{north east}}% |
| 176 | +\pgf@xa=\pgf@x \pgf@ya=\pgf@y |
| 177 | +% desired size |
| 178 | +\pgf@process{\pgfpointdiff{#1}{#2}}% |
| 179 | +\pgf@xb=\pgf@x \pgf@yb=\pgf@y |
| 180 | +\pgfmathparse{min(\pgf@xb/\pgf@xa,\pgf@yb/\pgf@ya)}% |
| 181 | +\pgfmathparse{min(1,\pgfmathresult)}% |
| 182 | +\pgftransformscale{\pgfmathresult}% |
| 183 | +% |
| 184 | +% current position |
| 185 | +\pgfpointanchor{current subpicture}{center}% |
| 186 | +\pgf@xa=\pgf@x \pgf@ya=\pgf@y |
| 187 | +% desired position |
| 188 | +% we scaled transform, so apply reverse scaling to argument |
| 189 | +\pgfmathparse{1/\pgfmathresult}% |
| 190 | +\pgf@process{\pgfpointscale{\pgfmathresult}{\pgfpointlineattime{0.5}{#1}{#2}}}% |
| 191 | +\pgf@xb=\pgf@x \pgf@yb=\pgf@y |
| 192 | +\pgfpointdiff{\pgfpoint{\pgf@xa}{\pgf@ya}}{\pgfpoint{\pgf@xb}{\pgf@yb}}% |
| 193 | +\pgftransformshift{\pgfpoint{\pgf@x}{\pgf@y}}% |
| 194 | +} |
| 195 | + |
| 196 | +% utility functions -- not currently used |
| 197 | + |
| 198 | +\def\pgfnodedelete#1{% |
| 199 | +\expandafter\global\expandafter\let\csname pgf@sh@ns@#1\endcsname\relax |
| 200 | +\expandafter\global\expandafter\let\csname pgf@sh@np@#1\endcsname\relax |
| 201 | +\expandafter\global\expandafter\let\csname pgf@sh@nt@#1\endcsname\relax |
| 202 | +\expandafter\global\expandafter\let\csname pgf@sh@pi@#1\endcsname\relax |
| 203 | +\expandafter\global\expandafter\let\csname pgf@sh@ma@#1\endcsname\relax |
| 204 | +} |
| 205 | + |
| 206 | +\def\pgfnodeifexists#1#2#3{% |
| 207 | +\expandafter\ifx\csname pgf@sh@ns@#1\endcsname\relax#3\else#2\fi |
| 208 | +} |
| 209 | + |
0 commit comments