grph-trf.mkiv / last modification: 2020-01-30 14:16
%D \module
%D   [       file=grph-trf,
%D        version=2006.08.26, % overhaul/split of 1997.03.31 core-fig
%D          title=\CONTEXT\ Graphic Macros,
%D       subtitle=Transformations,
%D         author=Hans Hagen,
%D           date=\currentdate,
%D      copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
%C
%C This module is part of the \CONTEXT\ macro||package and is
%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
%C details.

\writestatus{loading}{ConTeXt Graphic Macros / Transformations}

\unprotect

%D We probably use too many dimens as the width calculations can go away. Some of
%D this is an inheritance of limited backends (some supported fractions, some
%D 1000's, some dimentions) so we calculate all of them. Nowadays scaling is always
%D available so we could simplify the code. On the other hand, we now get some extra
%D values for free.
%D
%D We could move the calculations to \LUA\ and clean up this lot anyway. On the
%D other hand, there is some danger of messing up so it has a real low priority.

% local:

\newdimen\d_grph_scale_x_size
\newdimen\d_grph_scale_y_size
\newdimen\d_grph_scale_x_offset
\newdimen\d_grph_scale_y_offset

\newdimen\d_grph_scale_h_size
\newdimen\d_grph_scale_v_size

\newconditional\c_grph_scale_done
\newconditional\c_grph_scale_scaling_done
\newconditional\c_grph_scale_limit_factors \settrue\c_grph_scale_limit_factors

\newconditional\c_grph_scale_swap_factor

\newdimen\d_grph_scale_wd
\newdimen\d_grph_scale_ht
\newdimen\d_grph_scale_dp

% global

\newdimen\d_grph_scale_used_x_size
\newdimen\d_grph_scale_used_y_size

\newcount\c_grph_scale_used_x_scale
\newcount\c_grph_scale_used_y_scale

\let     \m_grph_scale_used_x_scale\!!plusone
\let     \m_grph_scale_used_y_scale\!!plusone

\newdimen\d_grph_scale_outer_v_size % we cannot manipulate any global vsize !

% scratch:

\let\m_grph_scale_temp  \empty
\let\m_grph_scale_temp_x\empty
\let\m_grph_scale_temp_y\empty

% public:

\let\finalscaleboxxscale \!!plusone
\let\finalscaleboxyscale \!!plusone
\let\finalscaleboxwidth  \!!zeropoint
\let\finalscaleboxheight \!!zeropoint

% we can let sx/sy win (first check)

\installcorenamespace{scale}
\installcorenamespace{scalegrid}
\installcorenamespace{scalenorm}
\installcorenamespace{scalefact}

\installcommandhandler \??scale {scale} \??scale % we can have instances

\setupscale
  [\c!sx=\scaleparameter\c!s,
   \c!sy=\scaleparameter\c!s,
   \c!s=1,
  %\c!scale=,
  %\c!xscale=,
  %\c!yscale=,
  %\c!width=,
  %\c!height=,
  %\c!lines=,
  %\c!factor=,
  %\c!hfactor=,
  %\c!wfactor=,
  %\c!grid=,
  %\c!equalwidth=,
  %\c!equalheight=,
   \c!maxwidth=\scaleparameter\c!width,
   \c!maxheight=\scaleparameter\c!height]

\unexpanded\def\scale{\dodoubleempty\grph_scale}

\def\grph_scale[#1][#2]%
  {\bgroup
   % this is quite common so we might make this a helper
   \ifsecondargument
     \edef\currentscale{#1}%
     \setupcurrentscale[#2]%
   \else\iffirstargument
     \doifelseassignment{#1}
       {\let\currentscale\empty
        \setupcurrentscale[#1]}
       {\edef\currentscale{#1}}%
   \else
     \let\currentscale\empty
   \fi\fi
   %
   \dowithnextboxcs\grph_scale_finish\hbox}

\def\grph_scale_finish
  {% todo: p_scale_
   \edef\p_scale      {\scaleparameter\c!scale      }%
   \edef\p_xscale     {\scaleparameter\c!xscale     }%
   \edef\p_yscale     {\scaleparameter\c!yscale     }%
   \edef\p_width      {\scaleparameter\c!width      }%
   \edef\p_height     {\scaleparameter\c!height     }%
   \edef\p_depth      {\scaleparameter\c!depth      }%
   \edef\p_lines      {\scaleparameter\c!lines      }%
   \edef\p_factor     {\scaleparameter\c!factor     }%
   \edef\p_hfactor    {\scaleparameter\c!hfactor    }%
   \edef\p_wfactor    {\scaleparameter\c!wfactor    }%
 % \edef\p_grid       {\scaleparameter\c!grid       }% used once
   \edef\p_maxwidth   {\scaleparameter\c!maxwidth   }%
   \edef\p_maxheight  {\scaleparameter\c!maxheight  }%
   \edef\p_sx         {\scaleparameter\c!sx         }%
   \edef\p_sy         {\scaleparameter\c!sy         }%
   \edef\p_equalwidth {\scaleparameter\c!equalwidth }%
   \edef\p_equalheight{\scaleparameter\c!equalheight}%
   %
   \d_grph_scale_dp\dp\nextbox
   \ifx\p_depth\v!no \ifzeropt\d_grph_scale_dp \else
     \setbox\nextbox\naturalhpack{\raise\d_grph_scale_dp\box\nextbox}% new
     \d_grph_scale_dp\dp\nextbox
   \fi \fi
   \d_grph_scale_wd\wd\nextbox
   \d_grph_scale_ht\ht\nextbox
   \d_grph_scale_dp\dp\nextbox
   %
   \glet\finalscaleboxxscale \!!plusone
   \glet\finalscaleboxyscale \!!plusone
   \xdef\finalscaleboxwidth {\the\d_grph_scale_wd}%
   \xdef\finalscaleboxheight{\the\d_grph_scale_ht}%
   %
   \forgetall
   \dontcomplain
   %
   \setfalse\c_grph_scale_done
   \grph_scale_calculate
   \ifconditional\c_grph_scale_done
     \grph_scale_apply
   \fi
   \grph_scale_position
   %
   \box\nextbox
   \egroup}

% \def\grph_scale_apply
%   {\d_grph_scale_wd\finalscaleboxxscale\d_grph_scale_wd
%    \d_grph_scale_ht\finalscaleboxyscale\d_grph_scale_ht
%    \d_grph_scale_dp\finalscaleboxyscale\d_grph_scale_dp
%    \setbox\nextbox\naturalhpack
%      {\dostartscaling \finalscaleboxxscale \finalscaleboxyscale
%       \smashedbox\nextbox
%       \dostopscaling}%
%    \wd\nextbox\d_grph_scale_wd
%    \ht\nextbox\d_grph_scale_ht
%    \dp\nextbox\d_grph_scale_dp}

\def\grph_scale_apply
  {\d_grph_scale_wd\finalscaleboxxscale\d_grph_scale_wd
   \d_grph_scale_ht\finalscaleboxyscale\d_grph_scale_ht
   \d_grph_scale_dp\finalscaleboxyscale\d_grph_scale_dp
   \ifdim\d_grph_scale_wd=\wd\nextbox
     \ifdim\d_grph_scale_ht=\ht\nextbox
       \ifdim\d_grph_scale_dp=\dp\nextbox
        % \grph_scale_apply_nop
       \else
          \grph_scale_apply_yes
       \fi
     \else
       \grph_scale_apply_yes
     \fi
   \else
     \grph_scale_apply_yes
   \fi}

\def\grph_scale_apply_yes
  {\setbox\nextbox\naturalhpack
     {\dostartscaling \finalscaleboxxscale \finalscaleboxyscale
      \smashedbox\nextbox
      \dostopscaling}%
   \wd\nextbox\d_grph_scale_wd
   \ht\nextbox\d_grph_scale_ht
   \dp\nextbox\d_grph_scale_dp}

\def\m_grph_scale_stamp_c{11}

\def\grph_scale_calculate
  {\ifdim\d_grph_scale_ht>\zeropoint \ifdim\d_grph_scale_wd>\zeropoint
     \edef\m_grph_scale_stamp_a{\p_scale\p_xscale\p_yscale\p_factor\p_wfactor\p_hfactor\p_lines\p_width\p_height}%
     \edef\m_grph_scale_stamp_b{\p_sx\p_sy}%
     \ifx\m_grph_scale_stamp_a\empty
       \ifx\m_grph_scale_stamp_b\m_grph_scale_stamp_c
         % no scaling, don't change this (previous attempts failed anyway)
         \insidefloattrue % trick
         \grph_scale_calculations_yes
       \else
         \grph_scale_check_sx_sy
         \grph_scale_calculations_nop
       \fi
     \else
       \ifx\m_grph_scale_stamp_b\empty
         % no need to check further
       \else
         \grph_scale_check_sx_sy
       \fi
       \grph_scale_calculations_yes
     \fi
   \fi \fi}

\def\grph_scale_check_sx_sy
  {\ifdim\p_sx\onepoint=\onepoint\else\edef\p_width {\the\dimexpr\p_sx\d_grph_scale_wd}\fi
   \ifdim\p_sy\onepoint=\onepoint\else\edef\p_height{\the\dimexpr\p_sy\d_grph_scale_ht}\fi}

\def\grph_scale_rounded#1%
  {\expandafter\expandafter\expandafter\grph_scale_rounded_indeed
     \expandafter\WITHOUTPT\the\dimexpr#1\points*100+32768\scaledpoint\relax.\relax}

\def\grph_scale_rounded_indeed#1.#2\relax{#1}

\def\grph_scale_calculations_nop
  {\settrue\c_grph_scale_done
   \xdef\finalscaleboxwidth {\the\dimexpr\p_sx\d_grph_scale_wd\relax}%
   \xdef\finalscaleboxheight{\the\dimexpr\p_sy\d_grph_scale_ht\relax}%
   \glet\finalscaleboxxscale\p_sx
   \glet\finalscaleboxyscale\p_sy
   \ifx\finalscaleboxxscale\empty\let\finalscaleboxxscale\!!plusone\fi
   \ifx\finalscaleboxyscale\empty\let\finalscaleboxyscale\!!plusone\fi}

\let\grph_scale_calculations_report\relax

\def\grph_scale_calculations_yes
  {\settrue\c_grph_scale_done
   % initial values
   \d_grph_scale_x_offset\zeropoint
   \d_grph_scale_y_offset\zeropoint
   \d_grph_scale_x_size  \d_grph_scale_wd
   \d_grph_scale_y_size  \d_grph_scale_ht % alleen ht wordt geschaald!
   % final values
   \global\d_grph_scale_used_x_size \zeropoint % see note * (core-fig)
   \global\d_grph_scale_used_y_size \zeropoint % see note * (core-fig)
   \c_grph_scale_used_x_scale       \plusone   % see note * (core-fig)
   \c_grph_scale_used_y_scale       \plusone   % see note * (core-fig)
   \let\m_grph_scale_used_x_scale   \!!plusone
   \let\m_grph_scale_used_y_scale   \!!plusone
   % preparations
   \setfalse\c_grph_scale_scaling_done
   \grph_scale_check_parameters
   % calculators
   % beware, they operate in sequence, and calculate missing dimensions / messy
   % grph_scale_by_nature % when? needed?
   \ifconditional\c_grph_scale_scaling_done\else\grph_scale_by_factor   \fi
   \ifconditional\c_grph_scale_scaling_done\else\grph_scale_by_scale    \fi
   \ifconditional\c_grph_scale_scaling_done\else\grph_scale_by_dimension\fi
   % used in actual scaling
   \xdef\finalscaleboxwidth  {\the\d_grph_scale_used_x_size}%
   \xdef\finalscaleboxheight {\the\d_grph_scale_used_y_size}%
   \glet\finalscaleboxxscale \m_grph_scale_used_x_scale
   \glet\finalscaleboxyscale \m_grph_scale_used_y_scale
   \grph_scale_calculations_report}

\setvalue{\??scalegrid\v!yes     }{\getnoflines   \d_grph_scale_used_y_size\edef\p_height{\the\noflines\lineheight}}
\setvalue{\??scalegrid\v!height  }{\getrawnoflines\d_grph_scale_used_y_size\edef\p_height{\the\dimexpr\noflines\lineheight+\strutdepth}}
\setvalue{\??scalegrid\v!depth   }{\getrawnoflines\d_grph_scale_used_y_size\edef\p_height{\the\dimexpr\noflines\lineheight-\strutdepth}}
\setvalue{\??scalegrid\v!halfline}{\getrawnoflines\d_grph_scale_used_y_size\edef\p_height{\the\dimexpr\noflines\lineheight+.5\lineheight}}
\setvalue{\??scalegrid\v!fit     }{\getrawnoflines\d_grph_scale_used_y_size\edef\p_height{\the\noflines\lineheight}}
\letvalue{\??scalegrid\empty     }\donothing

\def\grph_scale_check_parameters % resolve self referencing loops
  {\ifx\p_maxwidth \empty\else \edef\p_maxwidth {\the\dimexpr\p_maxwidth        }\fi
   \ifx\p_maxheight\empty\else \edef\p_maxheight{\the\dimexpr\p_maxheight       }\fi
   \ifx\p_lines    \empty\else \edef\p_height   {\the\dimexpr\p_lines\lineheight}\fi
   \csname\??scalegrid\scaleparameter\c!grid\endcsname}

\def\grph_scale_by_nature % where ! ! ! ! !
  {\ifx\p_width \empty\else \global\d_grph_scale_used_x_size\p_width \fi
   \ifx\p_height\empty\else \global\d_grph_scale_used_y_size\p_height\fi
   \ifx\p_scale \empty\else        \c_grph_scale_used_x_scale\p_scale
                                   \c_grph_scale_used_y_scale\p_scale \fi
   \ifx\p_xscale\empty\else        \c_grph_scale_used_x_scale\p_xscale\fi
   \ifx\p_yscale\empty\else        \c_grph_scale_used_y_scale\p_yscale\fi}

% \defineexternalfigure[width-6][factor=auto,maxwidth=\textheight,maxheight=\textwidth]
% \defineexternalfigure[width-7][factor=auto,maxwidth=\textwidth,maxheight=\textheight]
% \placefigure{none}{\rotate[frame=on,offset=overlay]{\externalfigure[t:/sources/cow.pdf][width-6]}} \page
% \placefigure{none}{\framed[frame=on,offset=overlay]{\externalfigure[t:/sources/cow.pdf][width-7]}}

%D The \typpe {min} option makes sure that the smallest available space determines
%D the max size (so we can get a bleed on the other axis):
%D
%D \startlinecorrection
%D \startcombination[nx=2,ny=2,distance=4cm]
%D     {\externalfigure[hacker][factor=max,maxwidth=6cm,maxheight=6cm]} {}
%D     {\externalfigure[mill]  [factor=max,maxwidth=6cm,maxheight=6cm]} {}
%D     {\externalfigure[hacker][factor=min,maxwidth=6cm,maxheight=6cm]} {}
%D     {\externalfigure[mill]  [factor=min,maxwidth=6cm,maxheight=6cm]} {}
%D \stopcombination
%D \stoplinecorrection

\def\m_grph_scale_factor_set{\v!min,\v!max,\v!fit,\v!broad,\v!auto} % can be an \edef

\def\grph_scale_by_factor
  {\doifelseinset\p_factor\m_grph_scale_factor_set
     \grph_scale_by_factor_a
     {\doifelseinset\p_hfactor\m_grph_scale_factor_set
        \grph_scale_by_factor_b
        {\doifelseinset\p_wfactor\m_grph_scale_factor_set
           \grph_scale_by_factor_c
           \grph_scale_by_factor_d}}}

\def\grph_scale_by_factor_a
  {\grph_scale_apply_size
   \ifdim\d_grph_scale_x_size >\d_grph_scale_y_size
     \grph_scale_calculate_norm  \d_grph_scale_used_x_size\p_factor\p_maxwidth\hsize\d_grph_scale_h_size
     \grph_scale_calculate_scales\d_grph_scale_used_x_size\d_grph_scale_x_size
     \d_grph_scale_used_y_size\m_grph_scale_used_x_scale\d_grph_scale_y_size
   \else
     \grph_scale_calculate_norm  \d_grph_scale_used_y_size\p_factor\p_maxheight\d_grph_scale_outer_v_size\d_grph_scale_v_size
     \grph_scale_calculate_scales\d_grph_scale_used_y_size\d_grph_scale_y_size
     \d_grph_scale_used_x_size\m_grph_scale_used_x_scale\d_grph_scale_x_size
   \fi
   \grph_scale_by_factor_indeed}

\def\grph_scale_by_factor_b
  {\grph_scale_apply_size
   \grph_scale_calculate_norm  \d_grph_scale_used_y_size\p_hfactor\p_maxheight\d_grph_scale_outer_v_size\d_grph_scale_v_size
   \grph_scale_calculate_scales\d_grph_scale_used_y_size\d_grph_scale_y_size
   \d_grph_scale_used_x_size\m_grph_scale_used_x_scale\d_grph_scale_x_size
   \grph_scale_by_factor_indeed}

\def\grph_scale_by_factor_c
  {\grph_scale_apply_size
   \grph_scale_calculate_norm  \d_grph_scale_used_x_size\p_wfactor\p_maxwidth\hsize\d_grph_scale_h_size
   \grph_scale_calculate_scales\d_grph_scale_used_x_size\d_grph_scale_x_size
   \d_grph_scale_used_y_size\m_grph_scale_used_x_scale\d_grph_scale_y_size
   \grph_scale_by_factor_indeed}

\def\grph_scale_by_factor_d
  {\grph_scale_calculate_norm\d_grph_scale_used_y_size\p_factor \p_height \textheight\d_grph_scale_v_size
   \grph_scale_calculate_norm\d_grph_scale_used_y_size\p_hfactor\p_height \textheight\d_grph_scale_v_size
   \grph_scale_calculate_norm\d_grph_scale_used_x_size\p_wfactor\p_width  \hsize     \hsize}

\def\grph_scale_by_factor_indeed
  {\grph_scale_calculate_fact\p_factor
   \settrue\c_grph_scale_scaling_done
   \ifconditional\c_grph_scale_limit_factors
     \ifdim\d_grph_scale_used_x_size\ifconditional\c_grph_scale_swap_factor<\else>\fi\d_grph_scale_h_size
       \global\d_grph_scale_used_y_size\zeropoint
       \global\d_grph_scale_used_x_size\d_grph_scale_h_size
     \else\ifdim\d_grph_scale_used_y_size\ifconditional\c_grph_scale_swap_factor<\else>\fi\d_grph_scale_v_size
       \global\d_grph_scale_used_x_size\zeropoint
       \global\d_grph_scale_used_y_size\d_grph_scale_v_size
     \fi\fi
   \fi
   \grph_scale_by_dimension}

\def\grph_scale_by_scale
  {\edef\m_grph_scale_temp{\p_scale\p_xscale\p_yscale}%
   \ifx\m_grph_scale_temp\empty \else
     \grph_scale_apply_scale\m_grph_scale_used_x_scale\p_xscale
     \grph_scale_apply_scale\m_grph_scale_used_y_scale\p_yscale
   % \global\d_grph_scale_used_x_size\m_grph_scale_used_x_scale\d_grph_scale_x_size\relax % no global needed here
   % \global\d_grph_scale_used_y_size\m_grph_scale_used_y_scale\d_grph_scale_y_size\relax % no global needed here
     % wrong: we need to recalculate the scale
     \global\d_grph_scale_used_x_size\zeropoint
     \global\d_grph_scale_used_y_size\zeropoint
     %
     \ifx\p_maxwidth\empty
       \ifx\p_maxheight\empty
       \else
         \ifdim\d_grph_scale_y_size>\p_maxheight\relax
           \global\d_grph_scale_used_y_size\p_maxheight
         \fi
       \fi
     \else
       \ifdim\d_grph_scale_x_size>\p_maxwidth\relax
          \global\d_grph_scale_used_x_size\p_maxwidth
       \fi
     \fi
   \fi}

\def\grph_scale_by_dimension
  {\ifdim\d_grph_scale_used_x_size>\zeropoint
     \ifdim\d_grph_scale_used_y_size>\zeropoint
       \grph_scale_by_dimension_a
     \else
       \grph_scale_by_dimension_b
     \fi
   \else
     \ifdim\d_grph_scale_used_y_size>\zeropoint
       \grph_scale_by_dimension_c
     \else
       \grph_scale_by_dimension_d
     \fi
   \fi}

\def\grph_scale_by_dimension_a
  {\grph_scale_by_dimension_indeed
     {\grph_scale_calculate_scale\m_grph_scale_used_y_scale\d_grph_scale_used_y_size\d_grph_scale_y_size
      \grph_scale_calculate_scale\m_grph_scale_used_x_scale\d_grph_scale_used_x_size\d_grph_scale_x_size}%
     {\grph_scale_calculate_scale\m_grph_scale_used_y_scale\d_grph_scale_used_y_size\d_grph_scale_y_size
      \grph_scale_calculate_scale\m_grph_scale_used_x_scale\d_grph_scale_used_x_size\d_grph_scale_x_size}%
     {\grph_scale_calculate_scale\m_grph_scale_used_y_scale\d_grph_scale_used_y_size\d_grph_scale_y_size
      \grph_scale_calculate_scale\m_grph_scale_used_x_scale\d_grph_scale_used_x_size\d_grph_scale_x_size}}

\def\grph_scale_by_dimension_b
  {\grph_scale_by_dimension_indeed
     {\grph_scale_calculate_scales\d_grph_scale_used_x_size\d_grph_scale_x_size
      \d_grph_scale_used_y_size\m_grph_scale_used_x_scale\d_grph_scale_y_size}%
     {\grph_scale_calculate_scales\d_grph_scale_used_x_size\d_grph_scale_x_size
      \d_grph_scale_used_y_size\m_grph_scale_used_x_scale\d_grph_scale_y_size}%
     {\grph_scale_calculate_scales\d_grph_scale_used_x_size\d_grph_scale_x_size
      \d_grph_scale_used_y_size\m_grph_scale_used_x_scale\d_grph_scale_y_size}}

\def\grph_scale_by_dimension_c
  {\grph_scale_by_dimension_indeed % weird .. three same cases
     {\grph_scale_calculate_scales\d_grph_scale_used_y_size\d_grph_scale_y_size
      \d_grph_scale_used_x_size\m_grph_scale_used_x_scale\d_grph_scale_x_size}%
     {\grph_scale_calculate_scales\d_grph_scale_used_y_size\d_grph_scale_y_size
      \d_grph_scale_used_x_size\m_grph_scale_used_x_scale\d_grph_scale_x_size}%
     {\grph_scale_calculate_scales\d_grph_scale_used_y_size\d_grph_scale_y_size
      \d_grph_scale_used_x_size\m_grph_scale_used_x_scale\d_grph_scale_x_size}}

\def\grph_scale_by_dimension_d
  {\grph_scale_by_dimension_indeed
     {\grph_scale_apply_scale\m_grph_scale_used_x_scale\p_xscale
      \grph_scale_apply_scale\m_grph_scale_used_y_scale\p_yscale
      \global\d_grph_scale_used_x_size\m_grph_scale_used_x_scale\d_grph_scale_x_size
      \global\d_grph_scale_used_y_size\m_grph_scale_used_y_scale\d_grph_scale_y_size}%
     {\grph_scale_calculate_scales\d_grph_scale_used_x_size\d_grph_scale_x_size
      \d_grph_scale_used_y_size\m_grph_scale_used_x_scale\d_grph_scale_y_size}%
     {\grph_scale_calculate_scales\d_grph_scale_used_y_size\d_grph_scale_y_size
      \d_grph_scale_used_x_size\m_grph_scale_used_x_scale\d_grph_scale_x_size}}

\def\grph_scale_by_dimension_indeed#1#2#3%
  {#1\relax
   \ifx\p_maxwidth\empty \else
     \ifdim\d_grph_scale_used_x_size>\p_maxwidth\relax
       \global\d_grph_scale_used_x_size\p_maxwidth
       #2\relax
     \fi
   \fi
   \ifx\p_maxheight\empty \else
     \ifdim\d_grph_scale_used_y_size>\p_maxheight\relax
       \global\d_grph_scale_used_y_size\p_maxheight
        #3\relax
     \fi
   \fi}

\def\grph_scale_calculate_norm#1#2% todo: swap 1 and 2 and pass one less
  {\csname\??scalenorm\ifcsname\??scalenorm#2\endcsname#2\else\s!unknown\fi\endcsname#1#2}

\def\grph_scale_calculate_fact#1%
  {\csname\??scalefact\ifcsname\??scalefact#1\endcsname#1\else\s!unknown\fi\endcsname}

%setvalue{\??scalenorm\v!min    }#1#2#3#4#5{\global#1#4}
\setvalue{\??scalenorm\v!max    }#1#2#3#4#5{\global#1#4}
\setvalue{\??scalenorm\v!fit    }#1#2#3#4#5{\global#1#5}
\setvalue{\??scalenorm\v!broad  }#1#2#3#4#5{\global#1\dimexpr#5-4\externalfigureparameter\c!bodyfont\relax}
\setvalue{\??scalenorm\s!unknown}#1#2#3#4#5{\global#1\dimexpr#2\dimexpr\externalfigureparameter\c!bodyfont/10\relax\relax} % brr ex
\setvalue{\??scalenorm\v!auto   }#1#2#3#4#5{\ifx#3\empty\else\global#1#3\fi}
\setvalue{\??scalenorm\empty    }#1#2#3#4#5{\ifx#3\empty\else\global#1#3\fi}
\setvalue{\??scalenorm\s!default}#1#2#3#4#5{\ifx#3\empty\else\global#1#3\fi}

\setvalue{\??scalefact\v!min    }{\global\settrue \c_grph_scale_swap_factor}
\setvalue{\??scalefact\s!unknown}{\global\setfalse\c_grph_scale_swap_factor}

% \setvalue{\??scalenorm\v!min    }#1#2#3#4#5% an ugly hack
%   {\ifdim\d_grph_scale_used_x_size>\d_grph_scale_h_size
%      \d_grph_scale_used_y_size\vsize
%    \else
%      \d_grph_scale_used_x_size\hsize
%    \fi}

\setvalue{\??scalenorm\v!min}#1#2#3#4#5% an ugly hack
  {\d_grph_scale_used_x_size\hsize
   \d_grph_scale_used_y_size\vsize}

\def\grph_scale_calculate_scales#1#2%
  {\edef\m_grph_scale_used_x_scale{\luaexpr{\number#1/\number#2}}%
   \let\m_grph_scale_used_y_scale\m_grph_scale_used_x_scale}

\def\grph_scale_calculate_scale#1#2#3%
  {\edef#1{\luaexpr{\number#2/\number#3}}}

\def\grph_scale_apply_scale#1#2% #1 = parameter / scale can be empty
  {% no overflow
   \edef#1{\luaexpr
     {\number
      \ifx#2\empty
        \ifx  \p_scale \empty     \plusthousand \else
        \ifnum\p_scale=\zerocount \plusthousand \else
                                  \p_scale      \fi\fi
      \else\ifnum#2=\zerocount
        \ifx  \p_scale \empty     \plusthousand \else
        \ifnum\p_scale=\zerocount \plusthousand \else
                                  \p_scale      \fi\fi
      \else
                                  #2%
      \fi\fi
      /1000}}}

\def\grph_scale_apply_size
  {\ifx\p_maxheight\empty
     \d_grph_scale_outer_v_size\textheight
     \ifinner
       \d_grph_scale_outer_v_size \vsize % \textheight =\vsize
       \scratchdimen\vsize % \scratchdimen=\textheight
     \else\ifinsidefloat
       \d_grph_scale_outer_v_size \vsize % \textheight =\vsize
       \scratchdimen\vsize % \scratchdimen=\textheight
     \else\ifinpagebody
       \d_grph_scale_outer_v_size \vsize % \textheight =\vsize
       \scratchdimen\vsize % \scratchdimen=\textheight
     \else % hm, there should be an option to force this
       \ifdim\pagegoal<\maxdimen
         \ifdim\pagetotal<\pagegoal
           \scratchdimen\dimexpr\pagegoal-\pagetotal\relax
         \else
           \scratchdimen\d_grph_scale_outer_v_size % \textheight
         \fi
       \else
         \scratchdimen\d_grph_scale_outer_v_size % \textheight
       \fi
     \fi\fi\fi
   \else
     \scratchdimen\p_maxheight
     \d_grph_scale_outer_v_size\scratchdimen
   \fi
   \ifx\p_height\empty
     \d_grph_scale_v_size\scratchdimen
   \else
     \d_grph_scale_v_size\p_height
   \fi
   \ifx\p_width\empty
     \d_grph_scale_h_size\hsize
   \else
     \d_grph_scale_h_size\p_width
   \fi}

% \startcombination
%     {\externalfigure[cow.pdf] [frame=on,height=3cm,equalwidth=6cm]} {a cow}
%     {\externalfigure[mill.png][frame=on,height=3cm,equalwidth=6cm]} {a mill}
% \stopcombination

\def\grph_scale_position
  {\ifx\p_equalwidth\empty \else
    \scratchdimen\p_equalwidth\relax
    \ifdim\d_grph_scale_wd<\scratchdimen
      \setbox\nextbox\naturalhpack to \scratchdimen{\hss\box\nextbox\hss}%
    \fi
   \fi
   \ifx\p_equalheight\empty \else
     \scratchdimen\p_equalheight\relax
     \ifdim\d_grph_scale_ht<\scratchdimen
       \setbox\nextbox\naturalvpack to \scratchdimen{\vss\box\nextbox\vss}%
     \fi
   \fi}

\unexpanded\def\fastscale#1%
  {\ifnum#1=1000\relax
     \expandafter\grph_scale_fast_nop
   \else
     \expandafter\grph_scale_fast_yes
   \fi{#1}}

\def\grph_scale_fast_nop#1%
  {\hbox}

\def\grph_scale_fast_yes#1%
  {\edef\finalscaleboxxscale{\withoutpt\the\dimexpr#1\onepoint/1000\relax}% brrr
   \let\finalscaleboxyscale\finalscaleboxxscale
   \dowithnextboxcs\grph_scale_fast_finish\hbox}

\def\grph_scale_fast_finish
  {\grph_scale_apply
   \box\nextbox
   \endgroup}

\unexpanded\def\fastsxsy#1#2%
  {\bgroup
   \edef\p_sx{#1}%
   \edef\p_sy{#2}%
   \dowithnextboxcs\grph_scale_fast_sx_xy_finish\hbox}

% \def\grph_scale_fast_sx_xy_finish
%   {\grph_scale_check_sx_sy
%    \grph_scale_calculations_nop
%    \grph_scale_apply
%    \box\nextbox
%    \egroup}

\def\grph_scale_fast_sx_xy_finish
  {\grph_scale_check_sx_sy
   \d_grph_scale_wd\wd\nextbox
   \d_grph_scale_ht\ht\nextbox
   \d_grph_scale_dp\dp\nextbox
   \grph_scale_calculations_nop
   \grph_scale_apply
   \box\nextbox
   \egroup}

%D \macros
%D   {clip, setupclipping}
%D
%D Although related to figures, clipping can be applied to arbitrary content. We can
%D use \METAPOST\ to provide a non rectangular clipping path.
%D
%D \starttyping
%D \startMPclip{fun}
%D   clip currentpicture to fullcircle
%D     shifted (.5,.5) xscaled \width yscaled \height ;
%D \stopMPclip
%D \stoptyping
%D
%D We get a rectangular piece of the figure when we say:
%D
%D \starttyping
%D \clip[x=2,y=1]{\externalfigure[photo]}
%D \stoptyping
%D
%D When we want to clip to the oval we defined a few lines ago, we say:
%D
%D \starttyping
%D \clip[nx=1,ny=1,x=1,y=1,mp=fun]{\externalfigure[photo]}
%D \stoptyping
%D
%D The general characteristics of clipping can be set up with
%D
%D \showsetup{setupclipping}

\installcorenamespace{clipping}

\installdirectcommandhandler \??clipping {clipping}

\unexpanded\def\clip
  {\dosingleempty\grph_clip}

\def\grph_clip[#1]% nb top->bottom left->right
  {\bgroup
   \iffirstargument
     \setupcurrentclipping[#1]%
   \fi
   \dowithnextboxcs\grph_clip_finish\hbox}

\def\grph_clip_finish
  {\doifelse{\clippingparameter\c!state}\v!start
     \grph_clip_yes_finish
     \grph_clip_nop_finish}

\def\grph_clip_yes_finish
  {\ifdim\clippingparameter\c!width>\zeropoint
     \scratchwidth  \clippingparameter\c!width
     \scratchxoffset\clippingparameter\c!hoffset
   \else
     \scratchwidth\dimexpr\wd\nextbox/\clippingparameter\c!nx\relax
     \scratchxoffset\dimexpr\clippingparameter\c!x\scratchwidth-\scratchwidth\relax
     \scratchwidth\clippingparameter\c!sx\scratchwidth
   \fi
   \relax % sure
   \ifdim\clippingparameter\c!height>\zeropoint
     \scratchheight\clippingparameter\c!height
     \scratchyoffset\dimexpr\ht\nextbox-\clippingparameter\c!voffset-\scratchheight\relax
   \else
     \scratchheight\dimexpr\ht\nextbox/\clippingparameter\c!ny\relax
     \scratchyoffset\dimexpr-\clippingparameter\c!y\scratchheight-\clippingparameter\c!sy\scratchheight+\scratchheight\relax
     \scratchheight\clippingparameter\c!sy\scratchheight
     \advance\scratchyoffset \ht\nextbox
   \fi
   \setbox\nextbox\naturalhpack
     {\advance\scratchxoffset -\clippingparameter\c!leftoffset  \relax
      \advance\scratchyoffset -\clippingparameter\c!bottomoffset\relax
      \hskip-\scratchxoffset
      \lower\scratchyoffset
      \box\nextbox}%
   \wd\nextbox\zeropoint
   \ht\nextbox\zeropoint
   \dp\nextbox\zeropoint
   \setbox\nextbox\naturalhpack
     {\advance\scratchwidth \dimexpr\clippingparameter\c!leftoffset  +\clippingparameter\c!rightoffset\relax
      \advance\scratchheight\dimexpr\clippingparameter\c!bottomoffset+\clippingparameter\c!topoffset  \relax
      \dostartclipping{\clippingparameter\c!mp}\scratchwidth\scratchheight
        \box\nextbox
      \dostopclipping}%
   \setbox\nextbox\naturalhpack
     {\hskip-\clippingparameter\c!leftoffset
      \lower \clippingparameter\c!bottomoffset
      \box\nextbox}%
   \wd\nextbox\scratchwidth
   \ht\nextbox\scratchheight
   \dp\nextbox\zeropoint
   \box\nextbox
   \egroup}

\def\grph_clip_nop_finish
  {\box\nextbox
   \egroup}

\setupclipping
  [\c!state=\v!start,
   \c!n=\plusone, % was \plustwo
   \c!nx=\clippingparameter\c!n,\c!x=\plusone,\c!sx=\plusone,
   \c!ny=\clippingparameter\c!n,\c!y=\plusone,\c!sy=\plusone,
   \c!width=\zeropoint,
   \c!height=\zeropoint,
   \c!hoffset=\zeropoint,
   \c!voffset=\zeropoint,
   \c!offset=\zeropoint,
   \c!leftoffset=\clippingparameter\c!offset,
   \c!rightoffset=\clippingparameter\c!offset,
   \c!topoffset=\clippingparameter\c!offset,
   \c!bottomoffset=\clippingparameter\c!offset,
   \c!mp=]

%D \startbuffer
%D \startuseMPgraphic{test}
%D   path p ; p := fullcircle scaled 4cm ;
%D   draw p withpen pencircle scaled 1cm ;
%D   setbounds currentpicture to boundingbox p ;
%D \stopuseMPgraphic
%D
%D \hbox to \hsize \bgroup
%D   \hss
%D   \ruledhbox{\useMPgraphic{test}}%
%D   \hss
%D   \ruledhbox{\clip{\useMPgraphic{test}}}%
%D   \hss
%D \egroup
%D \stopbuffer
%D
%D \typebuffer \getbuffer

%D Mirroring.

\unexpanded\def\mirror
  {\bgroup
   \dowithnextboxcs\grph_mirror_finish\hbox}

\def\grph_mirror_finish
  {\scratchdimen\wd\nextbox
   % better use an hbox (if no \forgetall, leftskip etc may creep in)
   %\setbox\nextbox\vbox{\forgetall\dostartmirroring\hskip-\wd\nextbox\box\nextbox\dostopmirroring}%
   \setbox\nextbox\naturalhpack
     {\dostartmirroring
      \hskip-\wd\nextbox
      \box\nextbox
      \dostopmirroring}%
   \wd\nextbox\scratchdimen
   \box\nextbox
   \egroup}

%D A couple of examples, demonstrating how the depth is taken care of:
%D
%D \startbuffer
%D test\rotate[frame=on, rotation=0]  {gans}%
%D test\rotate[frame=on, rotation=90] {gans}%
%D test\rotate[frame=on, rotation=180]{gans}%
%D test\rotate[frame=on, rotation=270]{gans}%
%D test
%D \stopbuffer
%D
%D \typebuffer \getbuffer
%D
%D When we rotate over arbitrary angles, we need to relocate the resulting box
%D because rotation brings that box onto the negative axis. The calculations (mostly
%D sin and cosine) need to be tuned for the way a box is packages (i.e. the refence
%D point). A typical example of drawing, scribbling, and going back to the days of
%D school math.
%D
%D We do a bit more calculations than needed, simply because that way it's easier to
%D debug the code.

\installcorenamespace {rotate}
\installcorenamespace {rotatelocation}
\installcorenamespace {rotatepreset}

\newdimen\d_grph_rotate_x_size
\newdimen\d_grph_rotate_y_size
\newdimen\d_grph_rotate_x_offset
\newdimen\d_grph_rotate_y_offset
\newdimen\d_grph_rotate_x_position
\newdimen\d_grph_rotate_y_position

\newdimen\d_grph_rotate_used_height

\let\d_grph_rotate_width \!!widtha
\let\d_grph_rotate_height\!!heighta
\let\d_grph_rotate_depth \!!deptha

\let\d_grph_rotate_saved_width \!!widthb
\let\d_grph_rotate_saved_height\!!heightb
\let\d_grph_rotate_saved_depth \!!depthb

\newconditional\c_grph_rotate_obey_depth
\newconditional\c_grph_rotate_not_fit
\newconditional\c_grph_rotate_center

\installframedcommandhandler \??rotate {rotate} \??rotate

\setuprotate
  [\c!rotation=90,
   \c!location=\v!normal,
   \c!width=\v!fit,
   \c!height=\v!fit,
   \c!offset=\v!overlay,
   \c!frame=\v!off]

\let\p_rotation_location\empty
\let\p_rotation_rotation\empty

\unexpanded\def\rotate % \bgroup: \rotate kan argument zijn
  {\bgroup
   \dosingleempty\grph_rotate}

\def\grph_rotate[#1]%
  {\iffirstargument
     \setupcurrentrotate[#1]%
   \fi
   \edef\p_rotation_location{\rotateparameter\c!location}%
   \edef\p_rotation_rotation{\rotateparameter\c!rotation}%
   \csname\??rotatelocation
     \ifcsname\??rotatelocation\p_rotation_location\endcsname\p_rotation_location\else\v!default\fi
   \endcsname}

\def\grph_rotate_framed
  {\resetrotateparameter\c!location
   \dowithnextboxcs\grph_rotate_finish\vbox
   \inheritedrotateframed}

\def\grph_rotate_normal
  {\dowithnextboxcs\grph_rotate_finish\vbox}

\def\grph_rotate_finish
  {\grph_rotate_finish_indeed
   \egroup}

\setvalue{\??rotatelocation\v!depth}%
  {\setfalse\c_grph_rotate_not_fit
   \setfalse\c_grph_rotate_center
   \settrue \c_grph_rotate_obey_depth
   \grph_rotate_normal}

\setvalue{\??rotatelocation\v!fit}%
  {\settrue \c_grph_rotate_not_fit
   \setfalse\c_grph_rotate_center
   \settrue \c_grph_rotate_obey_depth
   \grph_rotate_normal}

\setvalue{\??rotatelocation\v!broad}%
  {\setfalse\c_grph_rotate_not_fit
   \setfalse\c_grph_rotate_center
   \setfalse\c_grph_rotate_obey_depth
   \grph_rotate_normal}

\setvalue{\??rotatelocation\v!high}%
  {\setfalse\c_grph_rotate_not_fit
   \setfalse\c_grph_rotate_center
   \setfalse\c_grph_rotate_obey_depth
   \grph_rotate_framed}

\setvalue{\??rotatelocation\v!middle}%
  {\setfalse\c_grph_rotate_not_fit
   \settrue \c_grph_rotate_center
   \setfalse\c_grph_rotate_obey_depth  % hm, depth ?
   \grph_rotate_normal}

\setvalue{\??rotatelocation\v!default}%
  {\setfalse\c_grph_rotate_not_fit
   \setfalse\c_grph_rotate_center
   \settrue \c_grph_rotate_obey_depth
   \grph_rotate_framed}

\unexpanded\def\dorotatebox#1% {angle} \hbox/\vbox/\vtop % a fast low level one
  {\ifcase#1\relax
     \expandafter\gobbleoneargument
   \else
     \expandafter\grph_rotate_box
   \fi{#1}}

\def\grph_rotate_box#1% {angle} \hbox/\vbox/\vtop
  {\bgroup
   \hbox\bgroup % compatibility hack
     \edef\p_rotation_rotation{#1}%
     \dowithnextboxcs\grph_rotate_finish_box}

\def\grph_rotate_finish_box
  {\setfalse\c_grph_rotate_not_fit      % this is the same as broad but
   \setfalse\c_grph_rotate_center       % without the following grab as
   \setfalse\c_grph_rotate_obey_depth   % we call finish directly
   \grph_rotate_finish_indeed
   \egroup
   \egroup}

\def\grph_rotate_finish_indeed
  {\naturalhpack\bgroup
     \ifx\p_rotation_rotation\empty
       \grph_rotate_finish_nop
     \else
       \grph_rotate_finish_yes
     \fi
   \egroup}

\def\grph_rotate_finish_nop
  {\boxcursor\box\nextbox}

\setvalue{\??rotatepreset\v!left}%
  {\edef\p_rotation_rotation{90}}

\setvalue{\??rotatepreset\v!right}%
  {\edef\p_rotation_rotation{270}}

\setvalue{\??rotatepreset\v!inner}%
  {\signalrightpage
   \doifelserightpage{\def\p_rotation_rotation{270}}{\def\p_rotation_rotation{90}}}

\setvalue{\??rotatepreset\v!outer}%
  {\signalrightpage
   \doifelserightpage{\def\p_rotation_rotation{90}}{\def\p_rotation_rotation{270}}}

\setvalue{\??rotatepreset\v!default}%
  {}

\def\grph_rotate_finish_yes
  {\begincsname\??rotatepreset\p_rotation_rotation\endcsname
   \setbox\nextbox\naturalvpack{\box\nextbox}% not really needed
   \dontcomplain
   \ifconditional\c_grph_rotate_center
     \d_grph_rotate_saved_width \wd\nextbox
     \d_grph_rotate_saved_height\ht\nextbox
     \d_grph_rotate_saved_depth \dp\nextbox
     \setbox\nextbox\naturalvpack{\vskip.5\ht\nextbox\hskip-.5\wd\nextbox\box\nextbox}%
     \smashbox\nextbox
   \fi
   \d_grph_rotate_width \wd\nextbox
   \d_grph_rotate_height\ht\nextbox
   \d_grph_rotate_depth \dp\nextbox
   \setbox\nextbox\naturalvpack{\naturalhpack{\raise\dp\nextbox\box\nextbox}}%
   \d_grph_rotate_used_height \ht\nextbox
   % much of the next happens in lua (all the sin and cos) so we can do that in
   % one go if needed
   \setcalculatedcos\cos\p_rotation_rotation
   \setcalculatedsin\sin\p_rotation_rotation
   \ifdim\sin\points>\zeropoint
     \ifdim\cos\points>\zeropoint
       \grph_rotate_calculate_a
       \grph_rotate_apply
     \else
       \grph_rotate_calculate_b
       \grph_rotate_apply
       \wd\nextbox\ifconditional\c_grph_rotate_not_fit\sin\d_grph_rotate_depth\else\d_grph_rotate_x_size\fi
     \fi
   \else
     \ifdim\cos\points<\zeropoint
       \grph_rotate_calculate_c
       \grph_rotate_apply
       \wd\nextbox\ifconditional\c_grph_rotate_not_fit\negated\sin\d_grph_rotate_height\else\d_grph_rotate_x_size\fi
     \else\ifdim\sin\points=\zeropoint
       \grph_rotate_calculate_d
       \grph_rotate_apply
       % no wd ?
     \else
       \grph_rotate_calculate_e
       \grph_rotate_apply
       \wd\nextbox\ifconditional\c_grph_rotate_not_fit\negated\sin\d_grph_rotate_height\else\d_grph_rotate_x_size\fi
     \fi\fi
   \fi
   \ifconditional\c_grph_rotate_center
     \setbox\nextbox\naturalvpack{\vskip-.5\d_grph_rotate_saved_height\hskip.5\d_grph_rotate_saved_height\box\nextbox}%
     \wd\nextbox\d_grph_rotate_saved_width
     \ht\nextbox\d_grph_rotate_saved_height
     \dp\nextbox\d_grph_rotate_saved_depth
   \fi
   \boxcursor\box\nextbox}

\def\grph_rotate_calculate_a
  {\d_grph_rotate_x_size\dimexpr\cos\d_grph_rotate_width+\sin\d_grph_rotate_used_height\relax
   \d_grph_rotate_y_size\dimexpr\sin\d_grph_rotate_width+\cos\d_grph_rotate_used_height\relax
   \d_grph_rotate_x_position\zeropoint
   \d_grph_rotate_y_position\cos\d_grph_rotate_used_height
   \ifconditional\c_grph_rotate_not_fit
     \d_grph_rotate_x_offset\dimexpr\negated\sin\d_grph_rotate_used_height+\sin\d_grph_rotate_depth\relax
   \fi
   \ifconditional\c_grph_rotate_obey_depth
     \d_grph_rotate_y_offset\cos\d_grph_rotate_depth
   \fi}

\def\grph_rotate_calculate_b
  {\d_grph_rotate_x_size\dimexpr\negated\cos\d_grph_rotate_width+\sin\d_grph_rotate_used_height\relax
   \d_grph_rotate_y_size\dimexpr\sin\d_grph_rotate_width+\negated\cos\d_grph_rotate_used_height\relax
   \d_grph_rotate_x_position\negated\cos\d_grph_rotate_width
   \d_grph_rotate_y_position\zeropoint
   \ifconditional\c_grph_rotate_not_fit
     \d_grph_rotate_x_offset\dimexpr-\d_grph_rotate_x_size+\sin\d_grph_rotate_depth\relax
   \fi
   \ifconditional\c_grph_rotate_obey_depth
     \d_grph_rotate_y_offset\negated\cos\d_grph_rotate_height
   \fi}

\def\grph_rotate_calculate_c
  {\d_grph_rotate_x_size\dimexpr\negated\cos\d_grph_rotate_width+\negated\sin\d_grph_rotate_used_height\relax
   \d_grph_rotate_y_size\dimexpr\negated\sin\d_grph_rotate_width+\negated\cos\d_grph_rotate_used_height\relax
   \d_grph_rotate_x_position\d_grph_rotate_x_size
   \d_grph_rotate_y_position\negated\sin\d_grph_rotate_width
   \ifconditional\c_grph_rotate_not_fit
     \d_grph_rotate_x_offset\dimexpr-\d_grph_rotate_x_size+\negated\sin\d_grph_rotate_height\relax
   \fi
   \ifconditional\c_grph_rotate_obey_depth
     \d_grph_rotate_y_offset\dimexpr\d_grph_rotate_y_size+\cos\d_grph_rotate_depth\relax
   \fi}

\def\grph_rotate_calculate_d
  {\d_grph_rotate_x_size\dimexpr\cos\d_grph_rotate_width+\negated\sin\d_grph_rotate_used_height\relax
   \d_grph_rotate_y_size\dimexpr\negated\sin\d_grph_rotate_width+\cos\d_grph_rotate_used_height\relax
   \d_grph_rotate_x_position\zeropoint
   \d_grph_rotate_y_position\d_grph_rotate_y_size
   \d_grph_rotate_x_offset\zeropoint
   \ifconditional\c_grph_rotate_obey_depth
     \d_grph_rotate_y_offset\d_grph_rotate_depth
   \fi}

\def\grph_rotate_calculate_e
  {\d_grph_rotate_x_size\dimexpr\cos\d_grph_rotate_width+\negated\sin\d_grph_rotate_used_height\relax
   \d_grph_rotate_y_size\dimexpr\negated\sin\d_grph_rotate_width+\cos\d_grph_rotate_used_height\relax
   \d_grph_rotate_x_position\negated\sin\d_grph_rotate_used_height
   \d_grph_rotate_y_position\d_grph_rotate_y_size
   \ifconditional\c_grph_rotate_not_fit
     \d_grph_rotate_x_offset\dimexpr-\d_grph_rotate_x_size+\negated\sin\d_grph_rotate_height\relax
   \fi
   \ifconditional\c_grph_rotate_obey_depth
     \d_grph_rotate_y_offset\negated\sin\d_grph_rotate_depth
   \fi}

\def\grph_rotate_apply
  {\setbox\nextbox\naturalvpack to \d_grph_rotate_y_size
     {\vfill
      \naturalhpack to \d_grph_rotate_x_size
        {\dostartrotation\p_rotation_rotation
           \wd\nextbox\zeropoint
           \ht\nextbox\zeropoint
           \box\nextbox
         \dostoprotation
         \hfill}%
      \kern\d_grph_rotate_y_position}%
   \setbox\nextbox\naturalhpack
     {\kern\dimexpr\d_grph_rotate_x_position+\d_grph_rotate_x_offset\relax
      \lower\d_grph_rotate_y_offset\box\nextbox}}

% \dostepwiserecurse{0}{360}{10}
%   {\startlinecorrection[blank]
%    \hbox
%      {\expanded{\setuprotate[rotation=\recurselevel]}%
%       \traceboxplacementtrue
%       \hbox to .2\hsize{\hss\ruledhbox{\rotate[location=depth] {\ruledhbox{\bfb  (depth)}}}}%
%       \hbox to .2\hsize{\hss\ruledhbox{\rotate[location=fit]   {\ruledhbox{\bfb    (fit)}}}}%
%       \hbox to .2\hsize{\hss\ruledhbox{\rotate[location=broad] {\ruledhbox{\bfb  (broad)}}}}%
%       \hbox to .2\hsize{\hss\ruledhbox{\rotate[location=normal]{\ruledhbox{\bfb (normal)}}}}%
%       \hbox to .2\hsize{\hss\ruledhbox{\rotate[location=high]  {\ruledhbox{\bfb   (high)}}}}}
%    \stoplinecorrection}

\protect \endinput