pgfsubpic.tex 7.5 KB
Newer Older
xiaotong committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190
%    pgfsubpic.tex
%    Version 1.1, 25 Dec 2009

%    Copyright 2009 by David Chiang

%    This program is free software; you can redistribute it and/or modify
%    it under the terms of the GNU General Public License as published by
%    the Free Software Foundation; either version 2 of the License, or
%    (at your option) any later version.

%    This program is distributed in the hope that it will be useful,
%    but WITHOUT ANY WARRANTY; without even the implied warranty of
%    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
%    GNU General Public License for more details.

%    You should have received a copy of the GNU General Public License along
%    with this program; if not, write to the Free Software Foundation, Inc.,
%    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.

% New in version 1.1:
% - the ability to save a subpicture in local variables
% - nodes in subpictures are tracked if the subpicture is placed with arbitrary transforms
% - new \pgffitsubpicture macro to transform a subpicture (preserving aspect) to fit in a desired box

% To do: 
% - speed up interpictureshift hook by promoting subsubpictures

\newdimen\pgf@subpicminx
\newdimen\pgf@subpicminy
\newdimen\pgf@subpicmaxx
\newdimen\pgf@subpicmaxy

% Special virtual node for current subpicture's bounding box
\expandafter\def\csname pgf@sh@ns@current subpicture\endcsname{rectangle}
\expandafter\def\csname pgf@sh@np@current subpicture\endcsname{%
  \def\southwest{\pgfqpoint{\pgf@subpicminx}{\pgf@subpicminy}}%
  \def\northeast{\pgfqpoint{\pgf@subpicmaxx}{\pgf@subpicmaxy}}%
}
\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
\expandafter\def\csname pgf@sh@pi@current subpicture\endcsname{\pgfpictureid}

% Create a pgfpicture inside an hbox for delayed placement
\def\pgfsubpicture{%
\expandafter\global\expandafter\setbox\pgf@hbox=\hbox\bgroup
\pgfinterruptpicture
\pgfpicture
\relax % not sure why. otherwise a curly brace immediately after causes an error
}

\def\endpgfsubpicture{
\global\pgf@subpicminx=\pgf@picminx
\global\pgf@subpicminy=\pgf@picminy
\global\pgf@subpicmaxx=\pgf@picmaxx
\global\pgf@subpicmaxy=\pgf@picmaxy
\global\edef\subpictureid{\pgfpictureid}%
\pgfsetbaseline{\pgf@picminy}%
\endpgfpicture%
\endpgfinterruptpicture%
\egroup
}

% Allocate registers for saving a subpicture. #1 is text, not a control sequence.
\def\pgfnewsubpicture#1{%
\expandafter\newbox\csname pgf@subpic@hbox@#1\endcsname
\expandafter\newdimen\csname pgf@subpic@minx@#1\endcsname
\expandafter\newdimen\csname pgf@subpic@miny@#1\endcsname
\expandafter\newdimen\csname pgf@subpic@maxx@#1\endcsname
\expandafter\newdimen\csname pgf@subpic@maxy@#1\endcsname
}

% saved subpictures are local to the current group
\def\pgfsavesubpicture#1{%
\expandafter\setbox\csname pgf@subpic@hbox@#1\endcsname\copy\pgf@hbox
\csname pgf@subpic@minx@#1\endcsname\pgf@subpicminx
\csname pgf@subpic@miny@#1\endcsname\pgf@subpicminy
\csname pgf@subpic@maxx@#1\endcsname\pgf@subpicmaxx
\csname pgf@subpic@maxy@#1\endcsname\pgf@subpicmaxy
\expandafter\edef\csname pgf@subpic@id@#1\endcsname{\subpictureid}
}

\def\pgfrestoresubpicture#1{%
\edef\act{\global\noexpand\setbox\pgf@hbox\noexpand\copy\csname pgf@subpic@hbox@#1\endcsname}\act
\expandafter\global\expandafter\pgf@subpicminx\csname pgf@subpic@minx@#1\endcsname
\expandafter\global\expandafter\pgf@subpicminy\csname pgf@subpic@miny@#1\endcsname
\expandafter\global\expandafter\pgf@subpicmaxx\csname pgf@subpic@maxx@#1\endcsname
\expandafter\global\expandafter\pgf@subpicmaxy\csname pgf@subpic@maxy@#1\endcsname
\xdef\subpictureid{\csname pgf@subpic@id@#1\endcsname}
}

% Place a previously-created subpicture, lining up its origin with the current origin
\def\pgfplacesubpicture{
\pgfscope
% expand current bounding box to accommodate subpicture
\pgfpathrectanglecorners{\pgfpoint{\the\pgf@subpicminx}{\the\pgf@subpicminy}}{\pgfpoint{\the\pgf@subpicmaxx}{\the\pgf@subpicmaxy}}%
\pgfusepath{use as bounding box}%
%
% make the subpicture a node in the containing picture
\expandafter\gdef\csname pgf@sh@ns@\subpictureid\endcsname{rectangle}
\expandafter\xdef\csname pgf@sh@np@\subpictureid\endcsname{%
  \noexpand\def\noexpand\southwest{\noexpand\pgfqpoint{\the\pgf@subpicminx}{\the\pgf@subpicminy}}%
  \noexpand\def\noexpand\northeast{\noexpand\pgfqpoint{\the\pgf@subpicmaxx}{\the\pgf@subpicmaxy}}%
}%
\pgfgettransform\pgf@temp
\expandafter\xdef\csname pgf@sh@nt@\subpictureid\endcsname{\pgf@temp}%
\expandafter\xdef\csname pgf@sh@pi@\subpictureid\endcsname{\pgfpictureid}%
%
% align origin of subpicture with origin
\pgftransformshift{\pgfpoint{\the\pgf@subpicminx}{\the\pgf@subpicminy}}%
\pgfqboxsynced{\pgf@hbox}%
\endpgfscope
}

% Hook onto existing macro \pgf@shape@interpictureshift.
% This is called whenever we look up an anchor of a node.
% This hook recursively checks to see if the node's picture
% is a subpicture of another, and if so, adjusts its position accordingly.

% This is slow. It makes drawing trees O(n^2) in the depth of the tree.
% The alternative is to store, for each picture, a list of the nodes
% inside it. But this way doesn't require us to hijack \pgfnode, and
% is robust to re-placement of a subpicture. A compromise would be
% to store, for each picture, a list of the *subpictures* inside it.

\let\orig@pgf@shape@interpictureshift\pgf@shape@interpictureshift
\def\unwind@subpic#1{%
% is #1 the current picture?
\edef\subpicid{#1}%
\ifx\subpicid\pgfpictureid
% yes, we're done
\else
% does #1 have a parent picture?
\expandafter\ifx\csname pgf@sh@pi@#1\endcsname\relax
% no, the original node was not inside the current picture
\fallback
\else
% yes, apply transform and move up to parent picture
{%
  \pgfsettransform{\csname pgf@sh@nt@#1\endcsname}%
  \pgf@pos@transform{\pgf@x}{\pgf@y}%
  \global\pgf@x=\pgf@x
  \global\pgf@y=\pgf@y
}%
\unwind@subpic{\csname pgf@sh@pi@#1\endcsname}%
\fi
\fi
}
\def\pgf@shape@interpictureshift#1{%
\edef\fallback{\pgf@x=\the\pgf@x\pgf@y=\the\pgf@y\noexpand\orig@pgf@shape@interpictureshift{#1}}%
\unwind@subpic{\csname pgf@sh@pi@#1\endcsname}%
}

% \pgffitsubpicture{sw}{ne}
% Make the subpicture fit in the rectangle from sw to ne, preserving its aspect ratio.
\def\pgffitsubpicture#1#2{%
% current size
\pgfpointdiff{\pgfpointanchor{current subpicture}{south west}}{\pgfpointanchor{current subpicture}{north east}}%
\pgf@xa=\pgf@x \pgf@ya=\pgf@y
% desired size
\pgf@process{\pgfpointdiff{#1}{#2}}%
\pgf@xb=\pgf@x \pgf@yb=\pgf@y
\pgfmathparse{min(\pgf@xb/\pgf@xa,\pgf@yb/\pgf@ya)}%
\pgfmathparse{min(1,\pgfmathresult)}%
\pgftransformscale{\pgfmathresult}%
%
% current position
\pgfpointanchor{current subpicture}{center}%
\pgf@xa=\pgf@x \pgf@ya=\pgf@y
% desired position
% we scaled transform, so apply reverse scaling to argument
\pgfmathparse{1/\pgfmathresult}%
\pgf@process{\pgfpointscale{\pgfmathresult}{\pgfpointlineattime{0.5}{#1}{#2}}}%
\pgf@xb=\pgf@x \pgf@yb=\pgf@y
\pgfpointdiff{\pgfpoint{\pgf@xa}{\pgf@ya}}{\pgfpoint{\pgf@xb}{\pgf@yb}}%
\pgftransformshift{\pgfpoint{\pgf@x}{\pgf@y}}%
}

% utility functions -- not currently used

\def\pgfnodedelete#1{%
\expandafter\global\expandafter\let\csname pgf@sh@ns@#1\endcsname\relax
\expandafter\global\expandafter\let\csname pgf@sh@np@#1\endcsname\relax
\expandafter\global\expandafter\let\csname pgf@sh@nt@#1\endcsname\relax
\expandafter\global\expandafter\let\csname pgf@sh@pi@#1\endcsname\relax
\expandafter\global\expandafter\let\csname pgf@sh@ma@#1\endcsname\relax
}

\def\pgfnodeifexists#1#2#3{%
\expandafter\ifx\csname pgf@sh@ns@#1\endcsname\relax#3\else#2\fi
}