sawine@9: % pgfsubpic.tex sawine@9: % Version 1.1, 25 Dec 2009 sawine@9: sawine@9: % Copyright 2009 by David Chiang sawine@9: sawine@9: % This program is free software; you can redistribute it and/or modify sawine@9: % it under the terms of the GNU General Public License as published by sawine@9: % the Free Software Foundation; either version 2 of the License, or sawine@9: % (at your option) any later version. sawine@9: sawine@9: % This program is distributed in the hope that it will be useful, sawine@9: % but WITHOUT ANY WARRANTY; without even the implied warranty of sawine@9: % MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the sawine@9: % GNU General Public License for more details. sawine@9: sawine@9: % You should have received a copy of the GNU General Public License along sawine@9: % with this program; if not, write to the Free Software Foundation, Inc., sawine@9: % 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. sawine@9: sawine@9: % New in version 1.1: sawine@9: % - the ability to save a subpicture in local variables sawine@9: % - nodes in subpictures are tracked if the subpicture is placed with arbitrary transforms sawine@9: % - new \pgffitsubpicture macro to transform a subpicture (preserving aspect) to fit in a desired box sawine@9: sawine@9: \newdimen\pgf@subpicminx sawine@9: \newdimen\pgf@subpicminy sawine@9: \newdimen\pgf@subpicmaxx sawine@9: \newdimen\pgf@subpicmaxy sawine@9: sawine@9: % Special virtual node for current subpicture's bounding box sawine@9: \expandafter\def\csname pgf@sh@ns@current subpicture\endcsname{rectangle} sawine@9: \expandafter\def\csname pgf@sh@np@current subpicture\endcsname{% sawine@9: \def\southwest{\pgfqpoint{\pgf@subpicminx}{\pgf@subpicminy}}% sawine@9: \def\northeast{\pgfqpoint{\pgf@subpicmaxx}{\pgf@subpicmaxy}}% sawine@9: } sawine@9: \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 sawine@9: \expandafter\def\csname pgf@sh@pi@current subpicture\endcsname{\pgfpictureid} sawine@9: sawine@9: % Create a pgfpicture inside an hbox for delayed placement sawine@9: \def\pgfsubpicture{% sawine@9: \expandafter\global\expandafter\setbox\pgf@hbox=\hbox\bgroup sawine@9: \pgfinterruptpicture sawine@9: \pgfpicture sawine@9: \relax % not sure why. otherwise a curly brace immediately after causes an error sawine@9: } sawine@9: sawine@9: \def\endpgfsubpicture{ sawine@9: \global\pgf@subpicminx=\pgf@picminx sawine@9: \global\pgf@subpicminy=\pgf@picminy sawine@9: \global\pgf@subpicmaxx=\pgf@picmaxx sawine@9: \global\pgf@subpicmaxy=\pgf@picmaxy sawine@9: \global\edef\subpictureid{\pgfpictureid}% sawine@9: \pgfsetbaseline{\pgf@picminy}% sawine@9: \endpgfpicture% sawine@9: \endpgfinterruptpicture% sawine@9: \egroup sawine@9: } sawine@9: sawine@9: % Allocate registers for saving a subpicture. #1 is text, not a control sequence. sawine@9: \def\pgfnewsubpicture#1{% sawine@9: \expandafter\newbox\csname pgf@subpic@hbox@#1\endcsname sawine@9: \expandafter\newdimen\csname pgf@subpic@minx@#1\endcsname sawine@9: \expandafter\newdimen\csname pgf@subpic@miny@#1\endcsname sawine@9: \expandafter\newdimen\csname pgf@subpic@maxx@#1\endcsname sawine@9: \expandafter\newdimen\csname pgf@subpic@maxy@#1\endcsname sawine@9: } sawine@9: sawine@9: % saved subpictures are local to the current group sawine@9: \def\pgfsavesubpicture#1{% sawine@9: \expandafter\setbox\csname pgf@subpic@hbox@#1\endcsname\box\pgf@hbox sawine@9: \csname pgf@subpic@minx@#1\endcsname\pgf@subpicminx sawine@9: \csname pgf@subpic@miny@#1\endcsname\pgf@subpicminy sawine@9: \csname pgf@subpic@maxx@#1\endcsname\pgf@subpicmaxx sawine@9: \csname pgf@subpic@maxy@#1\endcsname\pgf@subpicmaxy sawine@9: \expandafter\edef\csname pgf@subpic@id@#1\endcsname{\subpictureid}% sawine@9: } sawine@9: sawine@9: % place current subpicture into named subpicture sawine@9: \def\pgfmergesubpicture#1{% sawine@9: \begin{pgfsubpicture} sawine@9: % place current subpicture sawine@9: \pgfplacesubpicture sawine@9: % override containing picture sawine@9: \expandafter\xdef\csname pgf@sh@pi@\subpictureid\endcsname{\csname pgf@subpic@id@#1\endcsname}% sawine@9: % copy contents of #1 sawine@9: \pgfrestoresubpicture{#1} sawine@9: \pgflowlevelobj{\pgftransformshift{\pgfqpoint{\the\pgf@subpicminx}{\the\pgf@subpicminy}}}{\pgfqbox\pgf@hbox} sawine@9: \pgfpathrectanglecorners{\pgfqpoint{\the\pgf@subpicminx}{\the\pgf@subpicminy}}{\pgfqpoint{\the\pgf@subpicmaxx}{\the\pgf@subpicmaxy}}% sawine@9: \pgfusepath{use as bounding box}% sawine@9: % sawine@9: \end{pgfsubpicture} sawine@9: \expandafter\setbox\csname pgf@subpic@hbox@#1\endcsname\box\pgf@hbox sawine@9: \csname pgf@subpic@minx@#1\endcsname\pgf@subpicminx sawine@9: \csname pgf@subpic@miny@#1\endcsname\pgf@subpicminy sawine@9: \csname pgf@subpic@maxx@#1\endcsname\pgf@subpicmaxx sawine@9: \csname pgf@subpic@maxy@#1\endcsname\pgf@subpicmaxy sawine@9: % but don't save the new picture id, keep the existing one sawine@9: } sawine@9: sawine@9: \def\pgfrestoresubpicture#1{% sawine@9: \edef\act{\global\noexpand\setbox\pgf@hbox\noexpand\box\csname pgf@subpic@hbox@#1\endcsname}\act sawine@9: \expandafter\global\expandafter\pgf@subpicminx\csname pgf@subpic@minx@#1\endcsname sawine@9: \expandafter\global\expandafter\pgf@subpicminy\csname pgf@subpic@miny@#1\endcsname sawine@9: \expandafter\global\expandafter\pgf@subpicmaxx\csname pgf@subpic@maxx@#1\endcsname sawine@9: \expandafter\global\expandafter\pgf@subpicmaxy\csname pgf@subpic@maxy@#1\endcsname sawine@9: \xdef\subpictureid{\csname pgf@subpic@id@#1\endcsname}% sawine@9: } sawine@9: sawine@9: % Place a previously-created subpicture, lining up its origin with the current origin sawine@9: \def\pgfplacesubpicture{ sawine@9: \pgfscope sawine@9: % expand current bounding box to accommodate subpicture sawine@9: \pgfpathrectanglecorners{\pgfqpoint{\the\pgf@subpicminx}{\the\pgf@subpicminy}}{\pgfqpoint{\the\pgf@subpicmaxx}{\the\pgf@subpicmaxy}}% sawine@9: \pgfusepath{use as bounding box}% sawine@9: % sawine@9: % make the subpicture a node in the containing picture sawine@9: \expandafter\gdef\csname pgf@sh@ns@\subpictureid\endcsname{rectangle}% sawine@9: \expandafter\xdef\csname pgf@sh@np@\subpictureid\endcsname{% sawine@9: \noexpand\def\noexpand\southwest{\noexpand\pgfqpoint{\the\pgf@subpicminx}{\the\pgf@subpicminy}}% sawine@9: \noexpand\def\noexpand\northeast{\noexpand\pgfqpoint{\the\pgf@subpicmaxx}{\the\pgf@subpicmaxy}}% sawine@9: }% sawine@9: \pgfgettransform\pgf@temp sawine@9: \expandafter\xdef\csname pgf@sh@nt@\subpictureid\endcsname{\pgf@temp}% sawine@9: \expandafter\xdef\csname pgf@sh@pi@\subpictureid\endcsname{\pgfpictureid}% sawine@9: % sawine@9: % align origin of subpicture with origin sawine@9: \pgftransformshift{\pgfqpoint{\the\pgf@subpicminx}{\the\pgf@subpicminy}}% sawine@9: \pgfqboxsynced{\pgf@hbox}% sawine@9: \endpgfscope sawine@9: } sawine@9: sawine@9: % Hook onto existing macro \pgf@shape@interpictureshift. sawine@9: % This is called whenever we look up an anchor of a node. sawine@9: % This hook recursively checks to see if the node's picture sawine@9: % is a subpicture of another, and if so, adjusts its position accordingly. sawine@9: sawine@9: % This is slow. It makes drawing trees O(n^2) in the depth of the tree. sawine@9: % The alternative is to store, for each picture, a list of the nodes sawine@9: % inside it. But this way doesn't require us to hijack \pgfnode, and sawine@9: % is robust to re-placement of a subpicture. A compromise would be sawine@9: % to store, for each picture, a list of the *subpictures* inside it. sawine@9: sawine@9: \let\orig@pgf@shape@interpictureshift\pgf@shape@interpictureshift sawine@9: \def\unwind@subpic#1{% sawine@9: % is #1 the current picture? sawine@9: \edef\subpicid{#1}% sawine@9: \ifx\subpicid\pgfpictureid sawine@9: % yes, we're done sawine@9: \else sawine@9: % does #1 have a parent picture? sawine@9: \expandafter\ifx\csname pgf@sh@pi@#1\endcsname\relax sawine@9: % no, the original node was not inside the current picture sawine@9: \fallback sawine@9: \else sawine@9: % yes, apply transform and move up to parent picture sawine@9: {% sawine@9: \pgfsettransform{\csname pgf@sh@nt@#1\endcsname}% sawine@9: \pgf@pos@transform{\pgf@x}{\pgf@y}% sawine@9: \global\pgf@x=\pgf@x sawine@9: \global\pgf@y=\pgf@y sawine@9: }% sawine@9: \unwind@subpic{\csname pgf@sh@pi@#1\endcsname}% sawine@9: \fi sawine@9: \fi sawine@9: } sawine@9: \def\pgf@shape@interpictureshift#1{% sawine@9: \edef\fallback{\pgf@x=\the\pgf@x\pgf@y=\the\pgf@y\noexpand\orig@pgf@shape@interpictureshift{#1}}% sawine@9: \unwind@subpic{\csname pgf@sh@pi@#1\endcsname}% sawine@9: } sawine@9: sawine@9: % \pgffitsubpicture{sw}{ne} sawine@9: % Make the subpicture fit in the rectangle from sw to ne, preserving its aspect ratio. sawine@9: \def\pgffitsubpicture#1#2{% sawine@9: % current size sawine@9: \pgfpointdiff{\pgfpointanchor{current subpicture}{south west}}{\pgfpointanchor{current subpicture}{north east}}% sawine@9: \pgf@xa=\pgf@x \pgf@ya=\pgf@y sawine@9: % desired size sawine@9: \pgf@process{\pgfpointdiff{#1}{#2}}% sawine@9: \pgf@xb=\pgf@x \pgf@yb=\pgf@y sawine@9: \pgfmathparse{min(\pgf@xb/\pgf@xa,\pgf@yb/\pgf@ya)}% sawine@9: \pgfmathparse{min(1,\pgfmathresult)}% sawine@9: \pgftransformscale{\pgfmathresult}% sawine@9: % sawine@9: % current position sawine@9: \pgfpointanchor{current subpicture}{center}% sawine@9: \pgf@xa=\pgf@x \pgf@ya=\pgf@y sawine@9: % desired position sawine@9: % we scaled transform, so apply reverse scaling to argument sawine@9: \pgfmathparse{1/\pgfmathresult}% sawine@9: \pgf@process{\pgfpointscale{\pgfmathresult}{\pgfpointlineattime{0.5}{#1}{#2}}}% sawine@9: \pgf@xb=\pgf@x \pgf@yb=\pgf@y sawine@9: \pgfpointdiff{\pgfpoint{\pgf@xa}{\pgf@ya}}{\pgfpoint{\pgf@xb}{\pgf@yb}}% sawine@9: \pgftransformshift{\pgfpoint{\pgf@x}{\pgf@y}}% sawine@9: } sawine@9: sawine@9: % utility functions -- not currently used sawine@9: sawine@9: \def\pgfnodedelete#1{% sawine@9: \expandafter\global\expandafter\let\csname pgf@sh@ns@#1\endcsname\relax sawine@9: \expandafter\global\expandafter\let\csname pgf@sh@np@#1\endcsname\relax sawine@9: \expandafter\global\expandafter\let\csname pgf@sh@nt@#1\endcsname\relax sawine@9: \expandafter\global\expandafter\let\csname pgf@sh@pi@#1\endcsname\relax sawine@9: \expandafter\global\expandafter\let\csname pgf@sh@ma@#1\endcsname\relax sawine@9: } sawine@9: sawine@9: \def\pgfnodeifexists#1#2#3{% sawine@9: \expandafter\ifx\csname pgf@sh@ns@#1\endcsname\relax#3\else#2\fi sawine@9: } sawine@9: