%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
%D go away. Some of this is an inheritance of limited backends
%D (some supported fractions, some 1000's, some dimentions) so
%D we calculate all of them. Nowadays scaling is always available
%D so we could simplify the code. On the other hand, we now get
%D some extra values for free.
%D
%D We could move the calculations to \LUA\ and clean up this
%D lot anyway. On the other hand, there is some danger of messing
%D 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

\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}

\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}

% we could have: \freezeparameter\c!scale etc (less backtracking when used multiple)

\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\hpack{\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
   %
   \global\let\finalscaleboxxscale \!!plusone
   \global\let\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\hpack
     {\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_report
%   {\writestatus
%      {scaled}%
%      {wd:\finalscaleboxwidth,ht:\finalscaleboxheight
%      ,xscale:\finalscaleboxxscale,yscale:\finalscaleboxyscale}}

% \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}%
%    \xdef\finalscaleboxxscale {\luaexpr{\number\c_grph_scale_used_x_scale/1000}}%
%    \xdef\finalscaleboxyscale {\luaexpr{\number\c_grph_scale_used_y_scale/1000}}%
%    \grph_scale_calculations_report}

\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]}}

\def\m_grph_scale_factor_set{\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\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\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\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\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_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
  {\settrue\c_grph_scale_scaling_done
   \ifconditional\c_grph_scale_limit_factors
     \ifdim\d_grph_scale_used_x_size>\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>\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\d_grph_scale_used_x_size\d_grph_scale_x_size\c_grph_scale_used_x_scale\p_xscale
%      \grph_scale_apply_scale\d_grph_scale_used_y_size\d_grph_scale_y_size\c_grph_scale_used_y_scale\p_yscale
%      \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_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\d_grph_scale_used_y_size\d_grph_scale_y_size\c_grph_scale_used_y_scale
%       \grph_scale_calculate_scale\d_grph_scale_used_x_size\d_grph_scale_x_size\c_grph_scale_used_x_scale}%
%      {\grph_scale_calculate_scale\d_grph_scale_used_y_size\d_grph_scale_y_size\c_grph_scale_used_y_scale
%       \grph_scale_calculate_scale\d_grph_scale_used_x_size\d_grph_scale_x_size\c_grph_scale_used_x_scale}%
%      {\grph_scale_calculate_scale\d_grph_scale_used_y_size\d_grph_scale_y_size\c_grph_scale_used_y_scale
%       \grph_scale_calculate_scale\d_grph_scale_used_x_size\d_grph_scale_x_size\c_grph_scale_used_x_scale}}
%
% \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\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\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\d_grph_scale_y_size}}
%
% \def\grph_scale_by_dimension_c
%   {\grph_scale_by_dimension_indeed
%      {\grph_scale_calculate_scales\d_grph_scale_used_y_size\d_grph_scale_y_size\d_grph_scale_used_x_size\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\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\d_grph_scale_x_size}}
%
% \def\grph_scale_by_dimension_d
%   {\grph_scale_by_dimension_indeed
%      {\grph_scale_apply_scale\d_grph_scale_used_x_size\d_grph_scale_x_size\c_grph_scale_used_x_scale\p_xscale
%       \grph_scale_apply_scale\d_grph_scale_used_y_size\d_grph_scale_y_size\c_grph_scale_used_y_scale\p_yscale}%
%      {\grph_scale_calculate_scales\d_grph_scale_used_x_size\d_grph_scale_x_size\d_grph_scale_used_y_size\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\d_grph_scale_x_size}}

\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}

\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}

% \def\grph_scale_calculate_scales#1#2#3#4%
%   {\scratchdimen\dimexpr#1/\dimexpr#2/\plusthousand\relax\relax
%    \c_grph_scale_used_x_scale\scratchdimen
%    \c_grph_scale_used_y_scale\scratchdimen
%   %\writestatus{!!!!}{\the#3/\luaexpr{\number\dimexpr#1\relax/\number\dimexpr#2/1000}}%
%    #3\dimexpr\c_grph_scale_used_x_scale\dimexpr#4/\plusthousand\relax\relax}
%
% \def\grph_scale_calculate_scale#1#2#3%
%   {#3\dimexpr#1/\dimexpr#2/\plusthousand\relax\relax}
%
% \def\grph_scale_apply_scale#1#2#3#4% #4 = parameter / scale can be empty
%   {\ifcase0#4\relax
%      \ifcase0\p_scale\relax
%        #3=\plusthousand
%      \else
%        #3=\p_scale
%      \fi
%    \else
%      #3=#4%
%    \fi
%    \relax % important ! still ?
%    \global#1\ifnum#3=\plusthousand#2\else\dimexpr#3\dimexpr#2/\plusthousand\relax\relax\fi
%    \relax}

\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}

% we could inline this:
%
% \grph_scale_calculate_scale\m_grph_scale_used_x_scale#1#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
  {\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}

%\def\grph_scale_convert_large_scale#1#2#3#4%
%  {\scratchdimen#1\relax
%   \ifnum#3=\plusthousand
%     % == scale 1
%   \else
%     % better 1000 100 10 ranges, evt round 2sp
%     \divide\scratchdimen \plusthousand
%     \multiply\scratchdimen #3\relax
%   \fi
%   \scratchdimen-\scratchdimen  % beter hier - dan in driver
%   \edef#2{\the\scratchdimen}%
%   \scratchcounter#3\relax
%   \ifnum\scratchcounter>\plustenthousand
%     \divide\scratchcounter\plusten
%     \scratchdimen\the\scratchcounter\points % \the ?
%   \else
%     \scratchdimen\the\scratchcounter\points % \the ?
%     \divide\scratchdimen\plusten
%   \fi
%   \edef#4{\withoutpt\the\scratchdimen}}
%
% \def\grph_scale_convert_large_scale#1#2#3#4% less overflow
%   {\edef#2{\ifnum#3=\plusthousand-\the\dimexpr#1\else\luaexpr{-\number#3*\number\dimexpr#1/1000}sp\fi}%
%    \edef#4{\luaexpr{\number#3/10}}}

% \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\hpack 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\vpack 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}

%D \macros
%D   {clip, setupclipping}
%D
%D Although related to figures, clipping can be applied to
%D arbitrary content. We can use \METAPOST\ to provide a non
%D 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,
%D 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\@@cpwidth>\zeropoint
%      \!!dimena\@@cpwidth
%      \!!dimenc\@@cphoffset
%    \else
%      \!!dimena\wd\nextbox
%      \divide\!!dimena \@@cpnx
%      \!!dimenc\@@cpx\!!dimena
%      \advance\!!dimenc -\!!dimena
%      \!!dimena\@@cpsx\!!dimena
%    \fi
%    \relax % sure
%    \ifdim\@@cpheight>\zeropoint
%      \!!dimenb\@@cpheight
%      \!!dimend\ht\nextbox
%      \advance\!!dimend -\@@cpvoffset
%      \advance\!!dimend -\!!dimenb
%    \else
%      \!!dimenb\ht\nextbox
%      \divide\!!dimenb \@@cpny
%      \!!dimend-\@@cpy\!!dimenb
%      \advance\!!dimend -\@@cpsy\!!dimenb
%      \advance\!!dimend \!!dimenb
%      \!!dimenb\@@cpsy\!!dimenb
%      \advance\!!dimend \ht\nextbox % dimend !
%    \fi
%    \setbox\nextbox\hbox                            % old
%      {\advance\!!dimenc -\@@cpleftoffset           % new !
%       \advance\!!dimend -\@@cpbottomoffset         % new ! % - added
%       \hskip-\!!dimenc\lower\!!dimend\box\nextbox}% old
%    \wd\nextbox\zeropoint
%    \ht\nextbox\zeropoint
%    \dp\nextbox\zeropoint
%    \setbox\nextbox\hbox
%      {\advance\!!dimena \@@cpleftoffset         % new !
%       \advance\!!dimena \@@cprightoffset        % new !
%       \advance\!!dimenb \@@cpbottomoffset       % new !
%       \advance\!!dimenb \@@cptopoffset          % new !
%       \dostartclipping\@@cpmp\!!dimena\!!dimenb % old
%         \box\nextbox
%       \dostopclipping}%
%    \setbox\nextbox\hbox                       % new !
%      {\!!dimena-\@@cpleftoffset               % new !
%       \!!dimenb \@@cpbottomoffset             % new ! % - removed
%       \hskip\!!dimena\lower\!!dimenb\box\nextbox}% new !
%    \wd\nextbox\!!dimena
%    \ht\nextbox\!!dimenb
%    \dp\nextbox\zeropoint
%    \box\nextbox
%    \egroup}

\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\hpack
     {\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\hpack
     {\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\hpack
     {\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\hpack{\dostartmirroring\hskip-\wd\nextbox\box\nextbox\dostopmirroring}%
   \wd\nextbox\scratchdimen
   \box\nextbox
   \egroup}

%D A couple of examples, demonstrating how the depth is
%D 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

% When we rotate over arbitrary angles, we need to relocate the
% resulting box because rotation brings that box onto the negative
% axis. The calculations (mostly sin and cosine) need to be tuned for
% the way a box is packages (i.e. the refence point). A typical example
% of drawing, scribbling, and going back to the days of school math.
%
% We do a bit more calculations than needed, simply because that way
% it's easier to debug the code.

% Cleanup in progress ... todo: less boxing

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

% we can alias these to \d_layers-* to save some dimens or maybe have a generic
% set of scratch variables

% maybe just \rotation_...

\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
  {\hbox\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{\doifelseoddpage{90}{270}}}

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

\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}%
  {\edef\p_rotation_rotation{\realnumber{\p_rotation_rotation}}}% get rid of leading zeros and spaces

\def\grph_rotate_finish_yes
  {\csname\??rotatepreset
     \ifcsname\??rotatepreset\p_rotation_rotation\endcsname\p_rotation_rotation\else\v!default\fi
   \endcsname
   \setbox\nextbox\vpack{\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\vpack{\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\vpack{\hpack{\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\vpack{\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\vpack to \d_grph_rotate_y_size
     {\vfill
      \hpack 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\hpack
     {\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
