exercises/solutions/pgfsubpic.tex
changeset 9 b5c5ecd64f6a
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/exercises/solutions/pgfsubpic.tex	Fri May 18 22:37:44 2012 +0200
     1.3 @@ -0,0 +1,209 @@
     1.4 +%    pgfsubpic.tex
     1.5 +%    Version 1.1, 25 Dec 2009
     1.6 +
     1.7 +%    Copyright 2009 by David Chiang
     1.8 +
     1.9 +%    This program is free software; you can redistribute it and/or modify
    1.10 +%    it under the terms of the GNU General Public License as published by
    1.11 +%    the Free Software Foundation; either version 2 of the License, or
    1.12 +%    (at your option) any later version.
    1.13 +
    1.14 +%    This program is distributed in the hope that it will be useful,
    1.15 +%    but WITHOUT ANY WARRANTY; without even the implied warranty of
    1.16 +%    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    1.17 +%    GNU General Public License for more details.
    1.18 +
    1.19 +%    You should have received a copy of the GNU General Public License along
    1.20 +%    with this program; if not, write to the Free Software Foundation, Inc.,
    1.21 +%    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
    1.22 +
    1.23 +% New in version 1.1:
    1.24 +% - the ability to save a subpicture in local variables
    1.25 +% - nodes in subpictures are tracked if the subpicture is placed with arbitrary transforms
    1.26 +% - new \pgffitsubpicture macro to transform a subpicture (preserving aspect) to fit in a desired box
    1.27 +
    1.28 +\newdimen\pgf@subpicminx
    1.29 +\newdimen\pgf@subpicminy
    1.30 +\newdimen\pgf@subpicmaxx
    1.31 +\newdimen\pgf@subpicmaxy
    1.32 +
    1.33 +% Special virtual node for current subpicture's bounding box
    1.34 +\expandafter\def\csname pgf@sh@ns@current subpicture\endcsname{rectangle}
    1.35 +\expandafter\def\csname pgf@sh@np@current subpicture\endcsname{%
    1.36 +  \def\southwest{\pgfqpoint{\pgf@subpicminx}{\pgf@subpicminy}}%
    1.37 +  \def\northeast{\pgfqpoint{\pgf@subpicmaxx}{\pgf@subpicmaxy}}%
    1.38 +}
    1.39 +\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
    1.40 +\expandafter\def\csname pgf@sh@pi@current subpicture\endcsname{\pgfpictureid}
    1.41 +
    1.42 +% Create a pgfpicture inside an hbox for delayed placement
    1.43 +\def\pgfsubpicture{%
    1.44 +\expandafter\global\expandafter\setbox\pgf@hbox=\hbox\bgroup
    1.45 +\pgfinterruptpicture
    1.46 +\pgfpicture
    1.47 +\relax % not sure why. otherwise a curly brace immediately after causes an error
    1.48 +}
    1.49 +
    1.50 +\def\endpgfsubpicture{
    1.51 +\global\pgf@subpicminx=\pgf@picminx
    1.52 +\global\pgf@subpicminy=\pgf@picminy
    1.53 +\global\pgf@subpicmaxx=\pgf@picmaxx
    1.54 +\global\pgf@subpicmaxy=\pgf@picmaxy
    1.55 +\global\edef\subpictureid{\pgfpictureid}%
    1.56 +\pgfsetbaseline{\pgf@picminy}%
    1.57 +\endpgfpicture%
    1.58 +\endpgfinterruptpicture%
    1.59 +\egroup
    1.60 +}
    1.61 +
    1.62 +% Allocate registers for saving a subpicture. #1 is text, not a control sequence.
    1.63 +\def\pgfnewsubpicture#1{%
    1.64 +\expandafter\newbox\csname pgf@subpic@hbox@#1\endcsname
    1.65 +\expandafter\newdimen\csname pgf@subpic@minx@#1\endcsname
    1.66 +\expandafter\newdimen\csname pgf@subpic@miny@#1\endcsname
    1.67 +\expandafter\newdimen\csname pgf@subpic@maxx@#1\endcsname
    1.68 +\expandafter\newdimen\csname pgf@subpic@maxy@#1\endcsname
    1.69 +}
    1.70 +
    1.71 +% saved subpictures are local to the current group
    1.72 +\def\pgfsavesubpicture#1{%
    1.73 +\expandafter\setbox\csname pgf@subpic@hbox@#1\endcsname\box\pgf@hbox
    1.74 +\csname pgf@subpic@minx@#1\endcsname\pgf@subpicminx
    1.75 +\csname pgf@subpic@miny@#1\endcsname\pgf@subpicminy
    1.76 +\csname pgf@subpic@maxx@#1\endcsname\pgf@subpicmaxx
    1.77 +\csname pgf@subpic@maxy@#1\endcsname\pgf@subpicmaxy
    1.78 +\expandafter\edef\csname pgf@subpic@id@#1\endcsname{\subpictureid}%
    1.79 +}
    1.80 +
    1.81 +% place current subpicture into named subpicture
    1.82 +\def\pgfmergesubpicture#1{%
    1.83 +\begin{pgfsubpicture}
    1.84 +% place current subpicture
    1.85 +\pgfplacesubpicture
    1.86 +% override containing picture
    1.87 +\expandafter\xdef\csname pgf@sh@pi@\subpictureid\endcsname{\csname pgf@subpic@id@#1\endcsname}%
    1.88 +% copy contents of #1
    1.89 +\pgfrestoresubpicture{#1}
    1.90 +\pgflowlevelobj{\pgftransformshift{\pgfqpoint{\the\pgf@subpicminx}{\the\pgf@subpicminy}}}{\pgfqbox\pgf@hbox}
    1.91 +\pgfpathrectanglecorners{\pgfqpoint{\the\pgf@subpicminx}{\the\pgf@subpicminy}}{\pgfqpoint{\the\pgf@subpicmaxx}{\the\pgf@subpicmaxy}}%
    1.92 +\pgfusepath{use as bounding box}%
    1.93 +%
    1.94 +\end{pgfsubpicture}
    1.95 +\expandafter\setbox\csname pgf@subpic@hbox@#1\endcsname\box\pgf@hbox
    1.96 +\csname pgf@subpic@minx@#1\endcsname\pgf@subpicminx
    1.97 +\csname pgf@subpic@miny@#1\endcsname\pgf@subpicminy
    1.98 +\csname pgf@subpic@maxx@#1\endcsname\pgf@subpicmaxx
    1.99 +\csname pgf@subpic@maxy@#1\endcsname\pgf@subpicmaxy
   1.100 +% but don't save the new picture id, keep the existing one
   1.101 +}
   1.102 +
   1.103 +\def\pgfrestoresubpicture#1{%
   1.104 +\edef\act{\global\noexpand\setbox\pgf@hbox\noexpand\box\csname pgf@subpic@hbox@#1\endcsname}\act
   1.105 +\expandafter\global\expandafter\pgf@subpicminx\csname pgf@subpic@minx@#1\endcsname
   1.106 +\expandafter\global\expandafter\pgf@subpicminy\csname pgf@subpic@miny@#1\endcsname
   1.107 +\expandafter\global\expandafter\pgf@subpicmaxx\csname pgf@subpic@maxx@#1\endcsname
   1.108 +\expandafter\global\expandafter\pgf@subpicmaxy\csname pgf@subpic@maxy@#1\endcsname
   1.109 +\xdef\subpictureid{\csname pgf@subpic@id@#1\endcsname}%
   1.110 +}
   1.111 +
   1.112 +% Place a previously-created subpicture, lining up its origin with the current origin
   1.113 +\def\pgfplacesubpicture{
   1.114 +\pgfscope
   1.115 +% expand current bounding box to accommodate subpicture
   1.116 +\pgfpathrectanglecorners{\pgfqpoint{\the\pgf@subpicminx}{\the\pgf@subpicminy}}{\pgfqpoint{\the\pgf@subpicmaxx}{\the\pgf@subpicmaxy}}%
   1.117 +\pgfusepath{use as bounding box}%
   1.118 +%
   1.119 +% make the subpicture a node in the containing picture
   1.120 +\expandafter\gdef\csname pgf@sh@ns@\subpictureid\endcsname{rectangle}%
   1.121 +\expandafter\xdef\csname pgf@sh@np@\subpictureid\endcsname{%
   1.122 +  \noexpand\def\noexpand\southwest{\noexpand\pgfqpoint{\the\pgf@subpicminx}{\the\pgf@subpicminy}}%
   1.123 +  \noexpand\def\noexpand\northeast{\noexpand\pgfqpoint{\the\pgf@subpicmaxx}{\the\pgf@subpicmaxy}}%
   1.124 +}%
   1.125 +\pgfgettransform\pgf@temp
   1.126 +\expandafter\xdef\csname pgf@sh@nt@\subpictureid\endcsname{\pgf@temp}%
   1.127 +\expandafter\xdef\csname pgf@sh@pi@\subpictureid\endcsname{\pgfpictureid}%
   1.128 +%
   1.129 +% align origin of subpicture with origin
   1.130 +\pgftransformshift{\pgfqpoint{\the\pgf@subpicminx}{\the\pgf@subpicminy}}%
   1.131 +\pgfqboxsynced{\pgf@hbox}%
   1.132 +\endpgfscope
   1.133 +}
   1.134 +
   1.135 +% Hook onto existing macro \pgf@shape@interpictureshift.
   1.136 +% This is called whenever we look up an anchor of a node.
   1.137 +% This hook recursively checks to see if the node's picture
   1.138 +% is a subpicture of another, and if so, adjusts its position accordingly.
   1.139 +
   1.140 +% This is slow. It makes drawing trees O(n^2) in the depth of the tree.
   1.141 +% The alternative is to store, for each picture, a list of the nodes
   1.142 +% inside it. But this way doesn't require us to hijack \pgfnode, and
   1.143 +% is robust to re-placement of a subpicture. A compromise would be
   1.144 +% to store, for each picture, a list of the *subpictures* inside it.
   1.145 +
   1.146 +\let\orig@pgf@shape@interpictureshift\pgf@shape@interpictureshift
   1.147 +\def\unwind@subpic#1{%
   1.148 +% is #1 the current picture?
   1.149 +\edef\subpicid{#1}%
   1.150 +\ifx\subpicid\pgfpictureid
   1.151 +% yes, we're done
   1.152 +\else
   1.153 +% does #1 have a parent picture?
   1.154 +\expandafter\ifx\csname pgf@sh@pi@#1\endcsname\relax
   1.155 +% no, the original node was not inside the current picture
   1.156 +\fallback
   1.157 +\else
   1.158 +% yes, apply transform and move up to parent picture
   1.159 +{%
   1.160 +  \pgfsettransform{\csname pgf@sh@nt@#1\endcsname}%
   1.161 +  \pgf@pos@transform{\pgf@x}{\pgf@y}%
   1.162 +  \global\pgf@x=\pgf@x
   1.163 +  \global\pgf@y=\pgf@y
   1.164 +}%
   1.165 +\unwind@subpic{\csname pgf@sh@pi@#1\endcsname}%
   1.166 +\fi
   1.167 +\fi
   1.168 +}
   1.169 +\def\pgf@shape@interpictureshift#1{%
   1.170 +\edef\fallback{\pgf@x=\the\pgf@x\pgf@y=\the\pgf@y\noexpand\orig@pgf@shape@interpictureshift{#1}}%
   1.171 +\unwind@subpic{\csname pgf@sh@pi@#1\endcsname}%
   1.172 +}
   1.173 +
   1.174 +% \pgffitsubpicture{sw}{ne}
   1.175 +% Make the subpicture fit in the rectangle from sw to ne, preserving its aspect ratio.
   1.176 +\def\pgffitsubpicture#1#2{%
   1.177 +% current size
   1.178 +\pgfpointdiff{\pgfpointanchor{current subpicture}{south west}}{\pgfpointanchor{current subpicture}{north east}}%
   1.179 +\pgf@xa=\pgf@x \pgf@ya=\pgf@y
   1.180 +% desired size
   1.181 +\pgf@process{\pgfpointdiff{#1}{#2}}%
   1.182 +\pgf@xb=\pgf@x \pgf@yb=\pgf@y
   1.183 +\pgfmathparse{min(\pgf@xb/\pgf@xa,\pgf@yb/\pgf@ya)}%
   1.184 +\pgfmathparse{min(1,\pgfmathresult)}%
   1.185 +\pgftransformscale{\pgfmathresult}%
   1.186 +%
   1.187 +% current position
   1.188 +\pgfpointanchor{current subpicture}{center}%
   1.189 +\pgf@xa=\pgf@x \pgf@ya=\pgf@y
   1.190 +% desired position
   1.191 +% we scaled transform, so apply reverse scaling to argument
   1.192 +\pgfmathparse{1/\pgfmathresult}%
   1.193 +\pgf@process{\pgfpointscale{\pgfmathresult}{\pgfpointlineattime{0.5}{#1}{#2}}}%
   1.194 +\pgf@xb=\pgf@x \pgf@yb=\pgf@y
   1.195 +\pgfpointdiff{\pgfpoint{\pgf@xa}{\pgf@ya}}{\pgfpoint{\pgf@xb}{\pgf@yb}}%
   1.196 +\pgftransformshift{\pgfpoint{\pgf@x}{\pgf@y}}%
   1.197 +}
   1.198 +
   1.199 +% utility functions -- not currently used
   1.200 +
   1.201 +\def\pgfnodedelete#1{%
   1.202 +\expandafter\global\expandafter\let\csname pgf@sh@ns@#1\endcsname\relax
   1.203 +\expandafter\global\expandafter\let\csname pgf@sh@np@#1\endcsname\relax
   1.204 +\expandafter\global\expandafter\let\csname pgf@sh@nt@#1\endcsname\relax
   1.205 +\expandafter\global\expandafter\let\csname pgf@sh@pi@#1\endcsname\relax
   1.206 +\expandafter\global\expandafter\let\csname pgf@sh@ma@#1\endcsname\relax
   1.207 +}
   1.208 +
   1.209 +\def\pgfnodeifexists#1#2#3{%
   1.210 +\expandafter\ifx\csname pgf@sh@ns@#1\endcsname\relax#3\else#2\fi
   1.211 +}
   1.212 +