1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1.2 +++ b/exercises/solutions/pgfsubpic.tex Sun Jun 03 20:15:28 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 +