% \iffalse meta-comment % % Copyright (C) 2015 Daan Leijen % ------------------------------------------------------- % % This file may be distributed and/or modified under the % conditions of the LaTeX Project Public License, either version 1.2 % of this license or (at your option) any later version. % The latest version of this license is in: % % http://www.latex-project.org/lppl.txt % % and version 1.2 or later is part of all distributions of LaTeX % version 1999/12/01 or later. % % \fi % % \iffalse %<*driver> \ProvidesFile{ellipse.dtx} % %\NeedsTeXFormat{LaTeX2e}[1999/12/01] %\ProvidesPackage{ellipse} %<*package> [2004/11/05 v1.0 .dtx ellipse file] % %\RequirePackage{pict2e} % %<*driver> \documentclass{ltxdoc} \usepackage{graphicx} \usepackage{ellipse}[2004/11/05] \usepackage{amsmath} \usepackage{amssymb} \usepackage{hyperref} \usepackage{lmodern} \usepackage{xcolor} \usepackage{wrapfig} \newcommand\kk[1]{\iota_{#1}} \newcommand\sk[1]{\rho_{#1}} \newcommand\sint[1]{\textit{sint}_{#1}} \newcommand\cost[1]{\textit{cost}_{#1}} \newcommand\sign[1]{\pm_{#1}} \newcommand\csqrt{\textit{csqrt}} \renewcommand\cos{\textit{cos}}\renewcommand\sin{\textit{sin}} \renewcommand\arctan{\textit{arctan}}\renewcommand\tan{\textit{tan}} \renewcommand\max{\textit{max}}\renewcommand\sin{\textit{sin}} \providecolor{teal}{HTML}{008080} \providecolor{purple}{HTML}{800080} \providecolor{navy}{HTML}{000080} \providecolor{maroon}{HTML}{800000} \providecolor{floralwhite}{HTML}{FFFAF0} \providecolor{ivory}{HTML}{FFFFF0} \providecolor{white}{HTML}{FFFFFF} \providecolor{transparent}{named}{white} \providecolor{gainsboro}{HTML}{DCDCDC} \hypersetup{colorlinks=true,citecolor=navy,linkcolor=navy,urlcolor=navy,filecolor=navy,bookmarksdepth=3,bookmarksopenlevel=1} \makeatletter \DeclareRobustCommand*\package[2][]{% \def\@tempa{#1}% \ifx\@tempa\@empty \textsf{#2}% \else \href{http://mirrors.ctan.org/macros/latex/#1}{\textsf{#2}}% \fi }% \DeclareRobustCommand*\pkgpicte{\package[contrib/pict2e/pict2e.pdf]{pict2e}} \newsavebox{\@ebox} \newcommand*\@unit[1]{\strip@pt\dimexpr#1\relax}% \newcommand*\ellipbox[1]{% \begingroup \savebox{\@ebox}{#1}% \setlength{\unitlength}{1pt}% \hspace*{0.8ex}% \begin{picture}(0,0)% \put(\@unit{0.5\wd\@ebox},\@unit{0.5\ht\@ebox - 0.5\dp\@ebox}){% \ellipse{\@unit{0.8ex + 0.5\wd\@ebox}}{\@unit{0.8ex + 0.5\ht\@ebox}}% }% \end{picture}% \usebox{\@ebox}% \hspace{0.8ex}% \endgroup% } \makeatother \EnableCrossrefs \CodelineIndex \RecordChanges \begin{document} \setlength\emergencystretch{3em} \DocInput{ellipse.dtx} \PrintChanges \setcounter{IndexColumns}{2}% \PrintIndex \end{document} % % \fi % % \CheckSum{0} % % \CharacterTable % {Upper-case \A\B\C\D\E\F\G\H\I\J\K\L\M\N\O\P\Q\R\S\T\U\V\W\X\Y\Z % Lower-case \a\b\c\d\e\f\g\h\i\j\k\l\m\n\o\p\q\r\s\t\u\v\w\x\y\z % Digits \0\1\2\3\4\5\6\7\8\9 % Exclamation \! Double quote \" Hash (number) \# % Dollar \$ Percent \% Ampersand \& % Acute accent \' Left paren \( Right paren \) % Asterisk \* Plus \+ Comma \, % Minus \- Point \. Solidus \/ % Colon \: Semicolon \; Less than \< % Equals \= Greater than \> Question mark \? % Commercial at \@ Left bracket \[ Backslash \\ % Right bracket \] Circumflex \^ Underscore \_ % Grave accent \` Left brace \{ Vertical bar \| % Right brace \} Tilde \~} % % % \changes{v1.0}{2015/03/24}{Initial version} % % \GetFileInfo{ellipse.dtx} % % \DoNotIndex{\newcommand,\renewcommand,\newenvironment,\renewenvironment} % \DoNotIndex{\providecommand,\def,\edef,\let,\gdef,\xdef,\global,\newtoks} % \DoNotIndex{\RequirePackage,\DeclareOption,\ProcessOptions,\ExecuteOptions} % \DoNotIndex{\input,\InputIfFileExists} % \DoNotIndex{\@ifdefinable,\@ifundefined,\@percentchar} % \DoNotIndex{\AtBeginDocument,\AtEndOfPackage} % \DoNotIndex{\PackageError,\PackageWarning,\PackageWarningNoLine,\PackageInfo} % \DoNotIndex{\MessageBreak,\typeout} % % \DoNotIndex{\z@,\z@skip,\p@,\@ne,\tw@,\thr@@,\@iv,\two@fourteen,\strip@pt} % \DoNotIndex{\the,\if,\else,\or,\fi,\ifnum,\ifdim,\ifcase,\ifodd} % \DoNotIndex{\advance,\multiply,\divide} % \DoNotIndex{\@tfor,\do} % \DoNotIndex{\csname,\endcsname,\begingroup,\endgroup} % \DoNotIndex{\expandafter,\afterassignment,\noexpand} % \DoNotIndex{\@ovxx,\@ovyy,\@ovdx,\@ovdy} % \DoNotIndex{\undefined,\dimexpr,\relax,\space,\protect,\begin} % \DoNotIndex{\@tempdima,\@tempdimb,\@tempdimc,\@tempdimd,\dimen@,\@tempa} % \DoNotIndex{\@tempswafalse,\@tempswatrue,\if@tempswa,\iffalse,\ifx,\ignorespaces} % % \title{The {\linethickness{0.6pt}\ellipbox{\textsf{ellipse}}} package} % \author{Daan Leijen \\ \texttt{daan@microsoft.com}} % % \maketitle % % \newcommand\pictexample[1]{% % \setlength{\unitlength}{10pt}% % \raisebox{-30pt}{\begin{picture}(6,8)% % \linethickness{0.4pt}\roundjoin% % \color{gainsboro}% % \put(0,0){\multiput(0,0)(1,0){9}{\line(0,1){6}}% % \multiput(0,0)(0,1){7}{\line(1,0){8}}% % }% % \put(0,0){\color{gray}% % {\vector(1,0){8}}\put(-0.5,6.5){$y$}% % {\vector(0,1){6}}\put(8.5,-0.5){$x$}% % }% % \color{black}% % \linethickness{0.8pt}% % #1\end{picture}}% % } % % \section{Introduction} % % \LaTeX{} has many advanced graphics packages now, the most extensive are % \href{https://www.ctan.org/pkg/pgf}{\textsf{tikz}} % and \href{https://www.ctan.org/pkg/pstricks-base}{\textsf{pstricks}}. % However, these are also large packages that take long to load and % may not always work on all drivers. The standard \pkgpicte{} package removes many of the previous % limitations of the `old' \LaTeX{} |picture| environment and makes it a \emph{lean % and portable} alternative to the more full featured packages. However, even though it can % draw circles and circle arcs well, it lacks the ability to draw ellipses and elliptical % arcs. This package adds these functions on top of the standard \pkgpicte{} primitives % (i.e. the |\cbezier| command). % % \section{Drawing ellipses} % % \noindent % \DescribeMacro\ellipse % \DescribeMacro{\ellipse*} % \marg{x-radius}\marg{y-radius}\\ % \strut\\ % These commands draw an ellipse with the specified radi\"\i. The |\ellipse| command draws % a stroked ellipse with the current |\linethickness| while |\ellipse*| draws a filled % ellipse with the current |\color|. For example: % % \noindent\begin{minipage}{0.7\linewidth}% % \begin{verbatim} % \setlength{\unitlength}{10pt}% % \begin{picture}(6,8) % \linethickness{0.8pt}% % \put(6,3){\color{teal}\ellipse*{2}{3}}% % \put(3,3){\color{blue}\ellipse{3}{2}}% % \end{picture} % \end{verbatim}\end{minipage} % \pictexample{% % \put(6,3){\color{teal}\ellipse*{2}{3}}% % \put(3,3){\color{blue}\ellipse{3}{2}}% % }\\[2ex] % % \noindent % \DescribeMacro\earc % \DescribeMacro{\earc*} % \oarg{start-angle$\rangle$|,|$\langle$end-angle}\marg{x-radius}\marg{y-radius}\\ % \strut\\ % These commands draw part of an ellipse with the specified radi\"\i. % The |\earc| command draws % a stroked elliptical arc with the current |\linethickness| while |\earc*| draws a filled % elliptical `pie slice' with the current |\color|. The optional argument specifies a start and % end-angle in degrees which must be between $-720$ and $720$ (but can be fractional). % The endings of the arcs are determined by the \emph{cap} setting: |\buttcap| (default), % |\roundcap| (add half disc), or |\squarecap| (add half square). % % \noindent\begin{minipage}{0.7\linewidth}% % \begin{verbatim} % \put(3,3){% % \color{blue}\roundcap\earc[135,330]{3}{2}}% % \put(6,3){% % \color{teal}\earc*[-45,90]{2}{3}}% % \end{verbatim} % \end{minipage} % \pictexample{% % \put(3,3){\color{blue}\roundcap\earc[135,330]{3}{2}}% % \put(6,3){\color{teal}\earc*[-45,90]{2}{3}}% % }\\[2ex] % % \noindent % \DescribeMacro\elliparc % \oarg{initial}\marg{center-x}\marg{center-y}\marg{x-rad}\marg{y-rad}\marg{start-angle}\marg{end-angle}\\ % % \noindent The core elliptical arc routine. These are to be used with path commands, like % |\lineto|, |\moveto|, |\strokepath|, etc, and can draw an elliptical arc at any center point. % The optional argument specifies the initial drawing % action: the default is $0$ (|\lineto|) which draws a line to the arc starting point, % the value $1$ % (|\moveto|) just moves to the starting point, and $2$ does nothing as an initial action. % If the start angle is larger than the end angle, the arc is drawn clockwise, and otherwise % anti-clockwise.\\[1ex] % % \noindent\begin{minipage}{0.7\linewidth}% % \begin{verbatim} % \elliparc[1]{3}{3}{3}{2}{90}{270}% % \elliparc{5}{3}{2}{2}{-90}{90}% % \closepath\strokepath % \color{teal}% % \moveto(1,3) % \elliparc{3}{3}{2}{1}{-135}{135}% % \closepath % \fillpath % \end{verbatim}\end{minipage} % \pictexample{% % \elliparc[1]{3}{3}{3}{2}{90}{270}% % \elliparc{5}{3}{2}{2}{-90}{90}% % \closepath\strokepath % \color{teal}% % \moveto(1,3) % \elliparc{3}{3}{2}{1}{-135}{135}% % \closepath % \fillpath % }\\[2ex] % % \noindent Note how the two initial arcs are automatically connected by a line % segment from $(3,1)$ to $(5,1)$ (due to the default optional argument of $0$ that % uses a |\lineto| command to the starting point of the arc). Similarly, we use % such initial line segment and a |\closepath| to draw the triangular side of the % inner ellipse. % % \subsection{Rotated ellipses} % % There is no direct command to rotate an ellipse but you can use the % standard |\rotatebox| command from the \package[required/graphics/grfguide.pdf]{graphicx} package. For example: % % \noindent\begin{minipage}{0.7\linewidth}% % \begin{verbatim} % \put(3,3){% % \rotatebox[origin=c]{45}{\ellipse{3}{2}}% % }% % \end{verbatim}\end{minipage} % \pictexample{% % \put(3,3){\rotatebox[origin=c]{45}{\ellipse{3}{2}}% % }% % }\\[2ex] % % \subsection{Using the picture environment inline} % % The standard \LaTeX{} |picture| environment is nowadays quite % powerful and convenient. Read the latest \pkgpicte{} documentation % and ``The unknown \emph{picture} environment'' \cite{picture} for more information. % One particularly nice feature is that we % can create a picture as |\begin{picture}(0,0)| to give it zero % space. This can be used for example to define an |\ellipbox| command % like:\\[1ex] % % \noindent\begin{minipage}{0.6\linewidth}% % \begin{verbatim} % Boxed numbers: % \ellipbox{1}, \ellipbox{123}. % \end{verbatim} % \end{minipage} % \begin{minipage}{0.38\linewidth}% % Boxed numbers: \ellipbox{1}, \ellipbox{123}. % \end{minipage}\\[1ex] % % \noindent We also used this command to draw the ellipse in the title % of this article, and it is defined as: % \begin{verbatim} %\newsavebox{\@ebox} %\newcommand*\@unit[1]{\strip@pt\dimexpr#1\relax}% %\newcommand*\ellipbox[1]{% % \begingroup % \savebox{\@ebox}{#1}% % \setlength{\unitlength}{1pt}% % \hspace*{0.8ex}% % \begin{picture}(0,0)% % \put(\@unit{0.5\wd\@ebox},\@unit{0.5\ht\@ebox - 0.5\dp\@ebox}){% % \ellipse{\@unit{0.8ex + 0.5\wd\@ebox}}{\@unit{0.8ex + 0.5\ht\@ebox}}% % }% % \end{picture}% % \usebox{\@ebox}\hspace{0.25ex}\endgroup} % \end{verbatim} % \noindent This is not the best code possible but it hopefully gives you a good % idea on how to implement your own boxes. Note the use of the |\@unit| macro % to convert dimensions to units, which is also why we need to set the |\unitlength| % to |1pt| here. % % \StopEventually{} % % \begin{thebibliography}{9} % \raggedright % % \bibitem{abst} M.~Abramowitz and I.A.~Stegun: \textit{Handbook of Mathematical Functions}, % \url{people.math.sfu.ca/~cbm/aands}, 1964 % % \bibitem{picture} Claudio Beccari: \emph{The unknown \emph{picture} environment}, % TUGBoat, vol. 33(1), 2012. \url{tug.org/TUGboat/tb33-1/tb103becc-picture.pdf} % % \bibitem{ellipse} Luc Maisonobe: \textit{Drawing an elliptical arc using polylines, quadratic or cubic B\'ezier lines.} % \url{www.spaceroots.org/documents/ellipse/elliptical-arc.pdf}, 2003 % % \bibitem{rajan:atan} S.~Rajan, Sichun Wang, R.~Inkol, and A.~Joyal: \textit{Efficient approximations for the arctangent function}. % In Signal Processing Magazine, vol. 23(3), pages 108--111, May 2006 % % \end{thebibliography} % % \clearpage % % \newcommand\abs[1]{\lvert #1\rvert} % \newcommand\rarg[1]{$\langle$\textit{#1}$\rangle$} % \newcommand\xellipse{\mathcal{E}} % % \section{Elliptical arcs as B\'ezier curves} % \begin{figure}\begin{center} % \setlength\unitlength{18pt} % \begin{picture}(12,9)(-1.5,-0.5)% % \linethickness{0.4pt}\roundjoin% % \iffalse % \color{gainsboro}% % \put(0,0){% % \multiput(-2,-1)(1,0){13}{\line(0,1){9}}% % \multiput(-2,-1)(0,1){10}{\line(1,0){12}}% % }% % \fi % \color{black}% % \put(0,0){% % \put(0,-0.5){\vector(0,1){8}}\put(8.5,-0.5){$x$}% % \put(-1.5,0){\vector(1,0){11}}\put(-0.5,6.5){$y$}% % }% % \put(4,3.3){\color{navy}\earc{5}{3} % \put(3,-2){$\xellipse$}%% % \color{teal}% % \put(-5.5,0){\line(1,0){11}}% % \put(0,-3.5){\line(0,1){8}}% % \put(3,0){\vector(-1,0){3}\vector(1,0){2}\raisebox{0.3ex}{$a$}}% % \put(0,-1.5){\vector(0,1){1.5}\vector(0,-1){1.5}$\,b$}% % \color{maroon}% % \put(0,0){\moveto(0,0)\elliparc{0}{0}{5}{3}{30}{120}\closepath\strokepath}% % {\linethickness{2pt}\earc[30,120]{5}{3}}% % \put(3.7,2.2){$p_1$}\put(-2,3.1){$p_2$}% % \put(2.25,2.92){\moveto(0,0)\lineto(1.35,-0.84)\strokepath\circle*{0.1}$\;q_1$}% % \put(-0.075,3.22){\moveto(0,0)\lineto(-1.56,-0.39)\strokepath\circle*{0.1}$\,q_2$}% % \color{black} % \linethickness{0.2pt}\arc[0,30]{1}\arc[0,120]{2}% % \put(1,0.2){$\alpha_1$}\put(1,1.8){$\alpha_2$}% % }% % \color{navy}\put(4,-0.3){$\,c_x$}\put(0,2.9){$\,c_y$}% % % % \end{picture}\end{center} % \caption{Approximating an elliptical arc with a cubic B\'ezier curve. The center of the ellipse % is at $(c_x,c_y)$ with a horizontal radius of $a$ and a vertical one $b$. The elliptical arc goes % from $\alpha_1$ to $\alpha_2$ and is approximated with a thick red cubic B\'ezier curve. The curve % starts at $p_1$ and ends in $p_2$ with two control points $q_1$ and $q_2$. The curve was drawn % using the command \texttt{\textbackslash{}elliparc\{4\}\{3.3\}\{5\}\{3\}\{30\}\{120\}}.} % \label{fig:ellipse} % \end{figure} % \begin{figure} % \setlength\unitlength{20pt}\begin{center} % \begin{picture}(12,8)(-1.5,-0.5)% % \linethickness{0.4pt}\roundjoin% % \iffalse % \put(0,0){\color{gainsboro}% % \multiput(-2,-1)(1,0){13}{\line(0,1){8}}% % \multiput(-2,-1)(0,1){9}{\line(1,0){12}}% % }% % \fi % \put(4,3){\color{navy}\earc{5}{3}\put(-4,1){$\xellipse$}\color{teal}% % \put(-5.5,0){\line(1,0){11}}% % \put(0,-3.5){\line(0,1){7}}% % \put(-2,0){\vector(-1,0){3}\vector(1,0){2}\raisebox{0.3ex}{$a$}}% % \put(0,-1.5){\vector(0,1){1.5}\vector(0,-1){1.5}$\,b$}% % \color{black}% % \put(0,0){\circle{6}}% % \put(0,0){\vector(3.6,2.08){3.6}\vector(2.16,2.08){2.16}}% % \put(0,0){\color{maroon}% % \put(3.6,0){\line(0,1){2.08}\circle*{0.1}}% % \put(3.2,-0.5){$a\cos(t_1)$}% % \put(2.16,0){\line(0,1){2.08}\circle*{0.1}}% % \put(1.3,-0.5){$\cos(t_1)$}% % }% % \linethickness{0.2pt}\arc[0,30]{1}\arc[0,43.9]{2}% % \put(1,0.2){$\alpha_1$}\put(1.65,1.25){$t_1$}% % }% % % % \end{picture}\end{center} % \caption{The relation between the parametric angle $t_1$ and the angle $\alpha_1$ to the point on the ellipse. % All points on the ellipse are defined by the parametric equation $\xellipse(t) = (c_x + a\cdot\cos(t), c_y + b\cdot\sin(t))$} % \label{fig:parametric} % \end{figure} % % % % % Drawing an ellipse or part of an ellipse (\emph{elliptical arc}) using B\'ezier % curves requires some math to determine the right control points of the B\'ezier curve. % Figure~\ref{fig:ellipse} establishes some notation. We do not consider rotated % ellipses here and always use $a$ for the $x$-radius and $b$ for the $y$-radius. % We are interested in finding the B\'ezier curve between the $\alpha_1$ and % $\alpha_2$ angles, which implies finding the starting % point $p_1$, the end point $p_2$ and the control points $q_1$ and $q_2$. % % Each point on an ellipse is determined by the following parametric equation: % \[ \xellipse(t) = (c_x + a\cdot\cos(t), c_y + b\cdot\sin(t)) \] % where $t$ is the % parametric angle. The parametric angle $t$ is just a property of the ellipse and has no % `real' counterpart. Figure~\ref{fig:parametric} gives some helpful intuition how % the $\alpha$ angles and $t$ angles are related: we can imagine drawing a unit circle inside % an ellipse where for every $t$ angle on the unit circle we have a corresponding point % and angle $\alpha$ on the ellipse. From the definition of $\xellipse$ it % is straightforward to derive a parametric angle $t_i$ for some $\alpha_i$: % \[ t_i = \arctan_2(\frac{\sin(\alpha_i)}{b},\frac{\cos(\alpha_i)}{a}) \] % % \noindent Given this relation, the start and end points of our curve are simply: % \begin{align*} % p_1 &= \xellipse(t_1)\\ % p_2 &= \xellipse(t_2) % \end{align*} % % \noindent To be able to calculate optimal control points $q$ we need to also determine the % tangent of each point on the ellipse, which is given by the derivative of $\xellipse$: % \[ \xellipse'(t) = (-a\cdot\sin(t), b\cdot\cos(t)) \] % % \noindent % The derivation of the optimal B\'ezier control points for an ellipse is quite involved, % see~\cite{ellipse} for a nice overview. % For a quadratic B\'ezier curve, it turns out the optimal control points are determined as: % \begin{align*} % q_1 &= p_1 + \tan(\frac{t_2 - t_1}{2})\cdot\xellipse'(t_1)\\ % &= p_2 - \tan(\frac{t_2 - t_1}{2})\cdot\xellipse'(t_2) % \end{align*} % % % \noindent while for a cubic B\'ezier curve, one solution for optimal control points is: % % % \begin{align*} % q_1 &= p_1 + \kappa\cdot\xellipse'(t_1)\\ % q_2 &= p_2 - \kappa\cdot\xellipse'(t_2)\\ % \kappa &= \sin(t_2 - t_1)\frac{\sqrt{4 + 3\tan^2(\frac{t_2 - t_1}{2})}-1}{3} % \end{align*} % \noindent We will use cubic bezier curves since they look best. However, a na\"\i{}ve implementation % may be too expensive in \LaTeX: if we count the expensive operations, we need about 11 $\cos$/$\sin$ % operations, plus a $\sqrt{}$ and 2 $\arctan$ operations. % % % \subsection{Optimizing elliptic arc equations} % Fortunately, we can improve upon this. First we note: % \begin{flalign*} % t_i &= \arctan_2(\frac{\sin(\alpha_i)}{b},\frac{\cos(\alpha_i)}{a}) & \hfill\\ % &= \arctan(\frac{a}{b}\tan(\alpha_i)) \\ % &= \arctan(\kk{i}) & \mbox{(introducing $\kk{i}$ for $\frac{a}{b}\tan(\alpha_i)$)} % \end{flalign*} % \noindent where we write $\kk{i}$ for $\frac{a}{b}\tan(\alpha_i)$. Now, % \begin{flalign*} % \cost{i} &= \cos(t_i)\\ % &= \cos(\arctan(\kk{i})) & \mbox{(geometry and pythagorean theorem)}\\ % &= \sign{i}\frac{1}{\sqrt{1 + \kk{i}^2}} \\ % \mbox{with}\\ % \sign{i} &= \textsf{if}\; \cos(\alpha_i) < 0\; \textsf{then}\; -\; \textsf{else}\; + % \end{flalign*} % Later we will see how we can efficiently calculate the square root term, but first % do the same derivation for the $\sin$ function: % \begin{flalign*} % \sint{i}&= \sin(t_i) \\ % &= \sin(\arctan(\frac{a}{b}\tan(\alpha_i))) & \\ % &= \sin(\arctan(\kk{i})) \\ % &= \sign{i}\frac{\kk{i}}{\sqrt{1 + \kk{i}^2}} % \end{flalign*} % \noindent Note that the interaction between the $\sin$ and $\kk{i}$ term (whose sign is determined by $\tan(\alpha_i)$) allows us to reuse the sign function used for $\cost{i}$. % % % Using the previous equalities we can restate the parametric equations in terms of $\sint{i}$ % and $\cost{i}$: % \begin{flalign*} % \xellipse_i &= (c_x + a\cdot\cost{i}), c_y + b\cdot\sint{i}) \\ % \xellipse'_i &= (-a\cdot\sint{i}, b\cdot\cost{i}) % \end{flalign*} % % % \noindent This takes care of $p_1$ and $p_2$. The control points $q$ still need $\sin(t_2 - t_1)$ and $\tan(\frac{t_2 - t_1}{2})$. % The halving rule on $\tan$ gives us: % \[ % \tan(\frac{t_2 - t_1}{2}) = \frac{1 - \cos(t_2 - t_1)}{\sin(t_2 - t_1)} \mbox{\quad(\cite[page 71, 4.3.20]{abst})} % \] % \noindent So that leaves $\sin(t_2 - t_1)$ and $\cos(t_2 - t_1)$. Using the addition laws it follows: % \begin{flalign*} % \sin(t_2 - t_1) = \sint2\cost1 - \cost2\sint1 & \mbox{\quad(\cite[page 72, 4.3.16]{abst})}\\ % \cos(t_2 - t_1) = \cost2\cost1 + \sint2\sint1 & \mbox{\quad(\cite[page 72, 4.3.17]{abst})} % \end{flalign*} % % \subsection{Circular square roots} % \noindent Now, we only need two $\tan$ operations to calculate the initial $\kk{1}$ and $\kk{2}$ terms but we still have % three square roots: $\sqrt{1 + \kk{i}^2}$ and $\sqrt{4 + 3\tan^2(\frac{t_2 - t_1}{2})}$. % Fortunately, both have the form $\sqrt{x^2 + y^2}$. For this form, we can make a very good % \begin{wrapfigure}[10]{o}{0.3\textwidth} % \setlength\unitlength{3pt} % \begin{picture}(30,30)(-15,-15)% % \linethickness{0.4pt}\roundjoin% % \iffalse % \put(0,0){\color{gainsboro}% % \multiput(-15,-15)(1,0){31}{\line(0,1){30}}% % \multiput(-15,-15)(0,1){31}{\line(1,0){30}}% % }% % \fi % \color{gainsboro}% % \put(0,0){\put(0,-15){\line(0,1){30}}% % \put(-15,0){\line(1,0){30}}% % }% % \color{black}% % \put(0,0){\circle{20}\put(-9,3){$\,x^2 + y^2$}% % \color{maroon}\moveto(14.14,0)\lineto(0,14.14)\lineto(-14.14,0)\lineto(0,-14.14) % \closepath\strokepath % \put(3,13){$\frac{1}{\sqrt{2}}(x+y)$}% % \color{navy}\moveto(10,10)\lineto(-10,10)\lineto(-10,-10)\lineto(10,-10) % \closepath\strokepath % \put(6,-12){$\,\max(x,y)$}% % } % % % \end{picture}\iffalse\caption{Estimating an initial value for $\sqrt{x^2+y^2}$}\fi % \end{wrapfigure} % initial guess for the square root, since this is the parametric equation for a circle. % The two good initial guesses form a `square' and `diamond' around this circle, namely % $\max(\abs{x},\abs{y})$ and $\frac{1}{\sqrt{2}}\abs{x+y}$. Each one can be superior depending if $x$ and $y$ % are close or not, but it can be shown that the best choice is always the largest of these. % Using this guess as an initial seed, we can do a standard Newton-Raphson iteration to % find a the square root where we only need 2 or 3 steps to achieve the desired precision. % Let's define a `circular square root' function $\csqrt$ such that % $\csqrt(x,y) \approx \sqrt{x^2 + y^2}$ as: % \begin{align*} % \csqrt(x,y) = &\textsf{let} & sqr & = x^2 + y^2 & \hfill \\ % & & x_0 & = \max(\abs{x},\abs{y},\frac{1}{\sqrt{2}}\abs{x+y}) & \\ % & & x_1 & = (x_0 + \frac{sqr}{x_0})/2 & \hspace{\textwidth}\\ % & & x_2 & = (x_1 + \frac{sqr}{x_1})/2 &\\ % & \textsf{in} & x_2 & % \end{align*} % % \subsection{The optimized elliptical B\'ezier equations} % \noindent Taking it all together, we get the following equations for a cubic B\'ezier curve approximation of an elliptical arc, % where we assume as input the center point $(c_x,c_y)$, the $x$- and $y$-radius $(a,b)$, and a start and end angle % $\alpha_1$ and $\alpha_2$. % It is assumed that $\alpha_1 \ne \alpha_2$ and $a \ge 0, b \ge 0$. Of course, % with bezier curves one should build a full ellipse of parts where for each part $\rvert\alpha_1 - \alpha_2\lvert \le 90$. % Given these parameters, the start and end point $p_1$ and $p_2$, and the control points $q_1$ and $q_2$ are % defined as: % \begin{flalign*} % p_1 &= \xellipse_1 & \\ % p_2 &= \xellipse_2\\ % q_1 &= p_1 + \kappa\cdot\xellipse'_1\\ % q_2 &= p_2 - \kappa\cdot\xellipse'_2\\ % \xellipse_i &= (c_x + a\cdot\cost{i}, c_y + b\cdot\sint{i})\\ % \xellipse'_i &= (-a\cdot\sint{i}, b\cdot\cost{i}) % \end{flalign*} % The $\cost{i}$ and $\sint{i}$ are calculated as: % \begin{flalign*} % \sint{i} & = \sign{i}\frac{\kk{i}}{\sk{i}} & \\ % \cost{i} & = \sign{i}\frac{1}{\sk{i}} % \end{flalign*} % with % \begin{flalign*} % \kk{i} &= \frac{a}{b}\tan(\alpha_i) & \\ % \sk{i} &= \csqrt(1,\kk{i}) \;\;(\approx \sqrt{1 + \kk{i}^2}) \\ % \sign{i} &= \textsf{if}\; \cos(\alpha_i) < 0\; \textsf{then}\; -\; \textsf{else}\; + % \end{flalign*} % And finally, the $\kappa$ term can be defined as: % \begin{flalign*} % \kappa &= \sint{21}\frac{\kappa_{sqrt}-1}{3} & % \end{flalign*} % with % \begin{flalign*} % \sint{21} & = \sint2\cost1 - \cost2\sint1 \;\;(=\sin(t_2 - t_1)) & \\ % \cost{21} & = \cost2\cost1 + \sint2\sint1 \;\;(=\cos(t_2 - t_1))\\ % \kappa_{tan} &= \frac{1 - \cost{21}}{\sint{21}} \;\;\mbox{(note: divides by zero if $\alpha_1 = \alpha_2$)}\\ % \kappa_{sqrt} &= \csqrt(\sqrt{4},\sqrt{3}\cdot\kappa_{tan}) \;\;(\approx \sqrt{4 + 3\kappa_{tan}^2}) % \end{flalign*} % %\section{Implementation} % % Generally, we use e-\TeX{} division to divide dimensions, where we % divide \rarg{dim$_1$} by \rarg{dim$_2$} using: % |\dimexpr 1pt * |\rarg{dim$_1$}|/|\rarg{dim$_2$}|\relax| % since it keeps a 64-bit intermediate result for such `scaling' expressions. % Note that both \rarg{dim} % expressions occur in an integer context and \TeX{} will convert % them to numbers automatically (i.e. in |sp| units). % % \subsection{Generic math and trigonometry routines} % % \begin{macro}{\pIIe@csedef} % \marg{csname}\textit{pattern}\marg{body}\\ % Define a macro by a \textit{csname}. Just like the |\csedef| function % from \textsf{etoolbox} package % \begin{macrocode} \providecommand*\pIIe@csedef[1]{\expandafter\edef\csname #1\endcsname} % \end{macrocode} % \end{macro} % %\begin{macro}{\pIIe@ellip@csqrt} % \marg{dimen$_x$}\marg{dimen$_y$}\marg{dimreg$_{res}$}\\ % Calculates $res \approx \sqrt{x^2 + y^2}$ and caches previous results % for efficiency. % % Overwrites |\@ovxx|,|\@ovyy|,|\@ovdx|,|\@ovdy|,|\@tempa|, and |\dimen@|. % \begin{macrocode} \newcommand*\pIIe@ellip@csqrt[3]{% \@ovxx=#1\relax \ifdim\@ovxx<\z@\@ovxx-\@ovxx\fi \@ovyy=#2\relax \ifdim\@ovyy<\z@\@ovyy-\@ovyy\fi \edef\pIIe@csname{@csqrt(\number\@ovxx,\number\@ovyy)}% \expandafter\ifx\csname\pIIe@csname\endcsname\relax \pIIe@ellip@csqrt@% \pIIe@csedef{\pIIe@csname}{\the\dimen@}% #3\dimen@ \else #3\dimexpr\csname\pIIe@csname\endcsname\relax \fi } % \end{macrocode} %\end{macro} %\begin{macro}{\pIIe@ellip@csqrt@} % Internal routine: calculates |\dimen@| $\approx \sqrt{x^2 + y^2}$. % where $x \ge 0$ and $y \ge 0$, and |\@ovxx| $=x$ and |\@ovyy| $=y$. % % Overwrites |\@ovdx|,|\@ovdy|, and |\@tempa|. % \begin{macrocode} \newcommand*\pIIe@ellip@csqrt@{% % \end{macrocode} % First determine $\max(x,y,\frac{1}{\sqrt{2}}(x+y))$ in |\dimen@|. % Put the sum $x+y$ in |\@ovdx|. % \begin{macrocode} \@ovdx\@ovxx \advance\@ovdx by \@ovyy % \end{macrocode} % Put initial guess in |\dimen@| $=\max(\abs{x},\abs{y},\frac{1}{\sqrt{2}}(x+y))$. % \begin{macrocode} \dimen@0.7071067\@ovdx \ifdim\dimen@<\@ovyy\dimen@\@ovyy\fi \ifdim\dimen@<\@ovxx\dimen@\@ovxx\fi % \end{macrocode} % To prevent overflowing \TeX{} dimensions we only do % a further Newton-Raphson approximation if the sum $x+y$ is less than 128pt. % Otherwise, for our application, the initial guess is still very precise since $x \ll y$ in that case. % \begin{macrocode} \ifdim\@ovdx<128\p@ % \end{macrocode} % Set |\@ovxx| to $x^2 + y^2$ % \begin{macrocode} \edef\@tempa{\strip@pt\@ovxx}% \@ovxx\@tempa\@ovxx \edef\@tempa{\strip@pt\@ovyy}% \@ovyy\@tempa\@ovyy \advance\@ovxx by \@ovyy % \end{macrocode} % Do two steps of Newton-Raphson (should we do three?) % \begin{macrocode} \advance\dimen@ by \dimexpr1pt * \@ovxx/\dimen@\relax \divide\dimen@ by 2% \advance\dimen@ by \dimexpr1pt * \@ovxx/\dimen@\relax \divide\dimen@ by 2% \fi % \end{macrocode} % Result is |\dimen@|. % \begin{macrocode} } % \end{macrocode} % \end{macro} % % \begin{macro}{\pIIe@atan@} % % Approximate the $\arctan$ using % \[x\cdot\frac{\pi}{4} - x \cdot (\abs{x} - 1) \cdot (0.2447 + 0.0663\cdot\abs{x})\] % This approximation was described by Rajan et al.~\cite{rajan:atan}. % % The \cmd{pIIe@atan@} computes the arctan of |\dimen@| which must be between $-1$ and $1$, and stores it in |\dimen@| again. % Overwrites |\@tempdim|(|a|,|b|,|c|,|d|),|\@tempa|, and |\dimen@|. % \begin{macrocode} \newcommand*\pIIe@atan@{% % \end{macrocode} % |\dimen@| contains $x$. % \begin{macrocode} \@tempdima\dimen@ % \end{macrocode} % Set |\@dimtmpb| to $\abs{x}$ % \begin{macrocode} \@tempdimb\@tempdima \ifdim\@tempdimb<\z@\@tempdimb-\@tempdimb\fi \dimen@0.0663\@tempdimb \advance\dimen@ 0.2447pt\relax \advance\@tempdimb -1pt\relax \edef\@tempa{\strip@pt\@tempdimb}% \dimen@\@tempa\dimen@ \edef\@tempa{\strip@pt\@tempdima}% \dimen@\@tempa\dimen@ \dimen@-\dimen@ % \end{macrocode} % Add $x\cdot\frac{\pi}{4}$ ($\approx 0.7853\cdot x$). % \begin{macrocode} \advance\dimen@ 0.7853\@tempdima } % \end{macrocode} % \end{macro} % \begin{macro}{\pIIe@atantwo} % \marg{dimen$_y$}\marg{dimen$_x$}\marg{dimreg$_{res}$}\\ % Calculate \rarg{res} $= \arctan_2(y,x)$ and caches the result for later use. % % Overwrites |\@tempdim|(|a|,|b|,|c|,|d|),|\@tempa|, and |\dimen@|. % Both $y$ and $x$ must be dimensions. % \begin{macrocode} \newcommand*\pIIe@atantwo[3]{% \edef\pIIe@csname{@atan2(\number\dimexpr#1\relax,\number\dimexpr#2\relax)}% \expandafter\ifx\csname\pIIe@csname\endcsname\relax \pIIe@atantwo@{#1}{#2}{#3}% \pIIe@csedef{\pIIe@csname}{\the\dimexpr#3\relax}% \else #3\dimexpr\csname\pIIe@csname\endcsname\relax \fi } % \end{macrocode} % \end{macro} % \begin{macro}{\pIIe@atantwo@} % \marg{dimen$_y$}\marg{dimen$_x$}\marg{dimreg$_{res}$}\\ % Calculate \rarg{res} $= \arctan_2(y,x)$. % Overwrites |\@tempdim|(|a|,|b|,|c|,|d|),|\@tempa|, and |\dimen@|. % Both $y$ and $x$ must be dimensions. % \begin{macrocode} \newcommand*\pIIe@atantwo@[3]{% \@tempdima\dimexpr#2\relax \@tempdimb\dimexpr#1\relax % \end{macrocode} % Handle extremes % \begin{macrocode} \ifdim\@tempdima=\z@\relax \ifdim\@tempdimb>\z@\relax\dimen@90\p@ \else\ifdim\@tempdimb<\z@\relax\dimen@-90\p@ \else\dimen@0\p@ \fi\fi \else % \end{macrocode} % Save angle adjustment term in |\@tempdimd|. % \begin{macrocode} \@tempdimd\z@ \ifdim\@tempdima<\z@\relax \ifdim\@tempdimb<\z@\relax\@tempdimd-180\p@ \else\@tempdimd180\p@ \fi \fi % \end{macrocode} % Divide $\frac{y}{x}$ and check if $-1 \le \frac{y}{x} \le 1$. % \begin{macrocode} \dimen@\dimexpr1pt * \@tempdimb/\@tempdima\relax \@tempdimc\dimen@ \ifdim\@tempdimc<\z@\relax\@tempdimc-\@tempdimc\fi \ifdim\@tempdimc>\p@\relax % \end{macrocode} % Use the equality $\arctan(x) = \pm\frac{1}{2}\pi - \arctan(\frac{1}{x})$ % to stay within the valid domain of |\pIIe@atan@|. The sign $\pm$ is % positive when $x \ge 0$ and negative otherwise. % \begin{macrocode} \dimen@\dimexpr1pt * \@tempdima/\@tempdimb\relax \ifdim\dimen@<\z@\relax\def\@tempsign{-}\else\def\@tempsign{}\fi \pIIe@atan@ \dimen@-\dimen@ \advance\dimen@ by \@tempsign1.5707pt\relax \else \pIIe@atan@ \fi % \end{macrocode} % And convert back to degrees ($\frac{180}{\pi} \approx 57.29578$) % \begin{macrocode} \dimen@57.29578\dimen@ % \end{macrocode} % Apply angle adjustment % \begin{macrocode} \advance\dimen@ by \@tempdimd \fi #3\dimen@% } % \end{macrocode} % \end{macro} % \subsection{Sub routines for drawing an elliptical arc} % % % \begin{macro}{\pIIe@noneto} % \marg{dimen$_x$}\marg{dimen$_y$}\\ % Ignores its arguments. Used as a no-op instead of |\pIIe@lineto| or |pIIe@moveto|. % \begin{macrocode} \newcommand*\pIIe@noneto[2]{} % \end{macrocode} % \end{macro} % %\begin{macro}{\pIIe@ellip@sincost@} % \marg{$\alpha_i$}\marg{$i$ = |one| or |two|}\\ % Calculate $\sint{i}$ and $\cost{i}$ into the |\@ellip|(|sin|/|cos|)$i$. % Assumes |\@ellipratio| $=\frac{a}{b}$. % \begin{macrocode} \newcommand*\pIIe@ellip@sincost@[2]{% % \end{macrocode} % Put the $\sin(\alpha_i)$ and $\cos(\alpha_i)$ into |\@tempdima| and |\@tempdimb|. % \begin{macrocode} \CalculateSin{#1}% \CalculateCos{#1}% \@tempdima\UseSin{#1}\p@ \@tempdimb\UseCos{#1}\p@ % \end{macrocode} % Check for extremes where $\tan = \pm\infty$. % \begin{macrocode} \ifdim\@tempdima=\p@\relax \pIIe@csedef{@ellipsin#2}{1}% \pIIe@csedef{@ellipcos#2}{0}% \else\ifdim\@tempdima=-\p@\relax \pIIe@csedef{@ellipsin#2}{-1}% \pIIe@csedef{@ellipcos#2}{0}% \else % \end{macrocode} % Calculate $\kk{i}$ in |\@tempdimc| and $\sqrt{1 + \kk{i}^2}$ in |\@tempdimd|, % and derive $\sint{i}$ and $\cost{i}$. % \begin{macrocode} \@tempdimc\@ellipratio\dimexpr1pt * \@tempdima/\@tempdimb\relax %\typeout{ i#2=\the\@tempdimc, sin(#1)=\the\@tempdima}% \pIIe@ellip@csqrt{\p@}{\@tempdimc}\@tempdimd \ifdim\@tempdimb<\z@\relax\@tempdimd-\@tempdimd\fi \pIIe@csedef{@ellipsin#2}{\strip@pt\dimexpr1pt * \@tempdimc/\@tempdimd\relax}% \pIIe@csedef{@ellipcos#2}{\strip@pt\dimexpr1pt * \p@/\@tempdimd\relax}% \fi\fi } % \end{macrocode} % \end{macro} %\begin{macro}{\pIIe@ellip@sincost} % \marg{$\alpha_1$}\marg{$\alpha_2$}\\ % Calculate $\sint{i}$ and $\cost{i}$ into the |\@ellip|(|sin|/|cos|)(|one|/|two|). % Assumes |\@ovro|$=a$ and |\@ovri|$=b$ with $b \ne 0$. % \begin{macrocode} \newcommand*\pIIe@ellip@sincost[2]{% % \end{macrocode} % Set |\@ellipratio| to the ratio $\frac{a}{b}$. % \begin{macrocode} %\typeout{ calc sin cos: angles (#1,#2), radii: (\the\@ovro,\the\@ovri)}% \edef\@ellipratio{\strip@pt\dimexpr1pt * \@ovro/\@ovri\relax}% % \end{macrocode} % And calculate $\sint{i}$ and $\cost{i}$ % \begin{macrocode} \pIIe@ellip@sincost@{#1}{one}% \pIIe@ellip@sincost@{#2}{two}% %\typeout{ sincos(a=#1)=(\@ellipsinone,\@ellipcosone), sincos(a=#2)=(\@ellipsintwo,\@ellipcostwo), }% } % \end{macrocode} % \end{macro} % % %\begin{macro}{\pIIe@omega} % \marg{$i$ = |one| or |two|}\\ % Calculates $\xellipse_i$ into |\@tempdima| and |\@tempdimb|. % Assumes |\@ovro|$=a$ and |\@ovri|$=b$. % \begin{macrocode} \newcommand*\pIIe@omega[3]{% \@tempdima\csname @ellipcos#3\endcsname\@ovro \advance\@tempdima by #1\relax \@tempdimb\csname @ellipsin#3\endcsname\@ovri \advance\@tempdimb by #2\relax } % \end{macrocode} % \end{macro} %\begin{macro}{\pIIe@omegai} % \marg{$i$ = |one| or |two|}\\ % Calculates $\xellipse'_i$ into |\@tempdimc| and |\@tempdimd|. % Assumes |\@ovro|$=a$ and |\@ovri|$=b$. % \begin{macrocode} \newcommand*\pIIe@omegai[1]{% \@tempdimc\csname @ellipsin#1\endcsname\@ovro \@tempdimc-\@tempdimc \@tempdimd\csname @ellipcos#1\endcsname\@ovri } % \end{macrocode} % \end{macro} %\begin{macro}{\pIIe@ellip@kappa} % Calculates $\kappa$, expects |\@ellip|(|sin|/|cos|)(|one|/|two|) to be defined. % \begin{macrocode} \newcommand*\pIIe@ellip@kappa{% % \end{macrocode} % Calculate $\sint{21}$ and $\cost{21}$ in |\@tempdima| and |\@tempdimb|. % \begin{macrocode} \@ovyy\@ellipsinone\p@ \@ovxx\@ellipcosone\p@ \@tempdima\@ellipcostwo\@ovyy \@tempdima-\@tempdima \advance\@tempdima by \@ellipsintwo\@ovxx \@tempdimb\@ellipcostwo\@ovxx \advance\@tempdimb by \@ellipsintwo\@ovyy % \end{macrocode} % First test if $\sint{21} = 0$ to prevent division by zero. In that % case, it must have been that $\alpha_1 = \alpha_2$ and we set $\kappa$ to zero % so it the control points become equal to the start and end point. % \begin{macrocode} \ifdim\@tempdima=\z@\relax \edef\@ellipkappa{0}% \else % \end{macrocode} % Calculate $\kappa_{tan}$ in |\dimen@| % \begin{macrocode} \dimen@\dimexpr1pt - \@tempdimb\relax \dimen@\dimexpr1pt * \dimen@/\@tempdima\relax % \end{macrocode} % Calculate $\kappa_{sqrt}$ in |\dimen@| % \begin{macrocode} \pIIe@ellip@csqrt{2\p@}{1.73205\dimen@}{\dimen@}% % \end{macrocode} % Calculate $\kappa$ in |\dimen@| % \begin{macrocode} \advance\dimen@ by -\p@ \divide\dimen@ by 3% \edef\@tempa{\strip@pt\@tempdima}% \dimen@\@tempa\dimen@ \edef\@ellipkappa{\strip@pt\dimen@}% \fi %\typeout{ calculated kappa: \@ellipkappa}% } % \end{macrocode} % \end{macro} % \subsection{Core routines for drawing elliptical arcs} % %\begin{macro}{\pIIe@elliparc@} % \oarg{start}\marg{$c_x$}\marg{$c_y$}\marg{$\alpha_1$}\marg{$\alpha_2$}\\ % Assumes that the radii are set as |\@ovro|$=a$ and |\@ovri|$=b$. % This is the main routine for drawing an elliptic arc, where $\abs{\alpha_2 - \alpha_1}\,\le 90$. % \begin{macrocode} \newcommand*\pIIe@elliparc@[5]{% %\typeout{elliparc: #1, center: (#2, #3), radius (\the\@ovro, \the\@ovri),angle (#4, #5)}% % \end{macrocode} % Define initial action: 0 (lineto), 1(moveto), or 2 (nothing) % \begin{macrocode} \ifcase #1\relax \let\@ellip@startto\pIIe@lineto \or \let\@ellip@startto\pIIe@moveto \or \let\@ellip@startto\pIIe@noneto% \else\PackageWarning{ellipse}{Illegal initial action in \protect\elliparc: % must be one of 0 (lineto), 1 (moveto) or 2 (do nothing) but I got: #1}% \fi % \end{macrocode} % Perform just the start action if the radii are zero % \begin{macrocode} \ifdim\@ovro=\z@\relax\@ovri\z@\fi \ifdim\@ovri=\z@\relax \@ellip@startto{#2}{#3}% \else % \end{macrocode} % Calculate $\sint{i}$ and $\cost{i}$ first into the |\@ellip|(|sin|/|cos|)(|one|/|two|) registers. % \begin{macrocode} \pIIe@ellip@sincost{#4}{#5}% % \end{macrocode} % And draw.. % \begin{macrocode} \pIIe@elliparc@draw{#2}{#3}% \fi } % \end{macrocode} % \end{macro} % %\begin{macro}{\pIIe@elliparc@t} % \oarg{start}\marg{$c_x$}\marg{$c_y$}\marg{$t_1$}\marg{$t_2$}\\ % Assumes that the radii are set as |\@ovro|$=a$ and |\@ovri|$=b$. % Moreover, this routine take $t_1$ and $t_2$ as the angles of the ellipse equation (instead of real angles $\alpha_i$). % This routine is mainly for other libraries that may already have computed the $t$ angles % and need a bit more efficiency. % \begin{macrocode} \newcommand*\pIIe@elliparc@t[5]{% % \end{macrocode} % Define initial action: 0 (lineto), 1(moveto), or 2 (nothing) % \begin{macrocode} \ifcase #1\relax \let\@ellip@startto\pIIe@lineto \or \let\@ellip@startto\pIIe@moveto \or \let\@ellip@startto\pIIe@noneto% \else\PackageWarning{ellipse}{Illegal initial action in \protect\elliparc: % must be one of 0 (lineto), 1 (moveto) or 2 (do nothing) but I got: #1}% \fi % \end{macrocode} % Perform just the start action if the radii are zero % \begin{macrocode} \ifdim\@ovro=\z@\relax\@ovri\z@\fi \ifdim\@ovri=\z@\relax \@ellip@startto{#2}{#3}% \else % \end{macrocode} % Calculate $\sint{i}$ and $\cost{i}$ first into the |\@ellip|(|sin|/|cos|)(|one|/|two|) registers. % \begin{macrocode} \CalculateSin{#4}\CalculateCos{#4}% \edef\@ellipsinone{\UseSin{#4}}% \edef\@ellipcosone{\UseCos{#4}}% \CalculateSin{#5}\CalculateCos{#5}% \edef\@ellipsintwo{\UseSin{#5}}% \edef\@ellipcostwo{\UseCos{#5}}% % \end{macrocode} % And draw.. % \begin{macrocode} \pIIe@elliparc@draw{#2}{#3}% \fi } % \end{macrocode} % \end{macro} % % \begin{macro}{pIIe@elliparc@draw} % \marg{$c_x$}\marg{$c_y$}\\ % Expects $a =$|\@ovro|, $b$ = |\@ovri|, and |\@ellip|(|sin|/|cos|)(|one|/|two|) defined. % |\@ellipstarto| should contain the initial drawing action and is called with an initial % $x$ and $y$ coordinate (usually equal to |\pIIe@lineto|,|\pIIe@moveto|, or |pIIe@noneto|). % \begin{macrocode} \newcommand*\pIIe@elliparc@draw[2]{% % Calculate $\kappa$. % \begin{macrocode} \pIIe@ellip@kappa% % \end{macrocode} % Now we are ready to compute the control points. First $p_1$. % \begin{macrocode} \pIIe@omega{#1}{#2}{one}% %\typeout{ point one: (\the\@tempdima,\the\@tempdimb)}% % \end{macrocode} % The coordinates are added to the path if and how necessary: % \begin{macrocode} \@ellip@startto\@tempdima\@tempdimb % \end{macrocode} % Add control point $q_1$ % \begin{macrocode} \pIIe@omegai{one}% \advance\@tempdima by \@ellipkappa\@tempdimc \advance\@tempdimb by \@ellipkappa\@tempdimd \pIIe@add@nums\@tempdima\@tempdimb %\typeout{ control one: (\the\@tempdima,\the\@tempdimb)}% % \end{macrocode} % Calculate $p_2$ % \begin{macrocode} \pIIe@omega{#1}{#2}{two}% % \end{macrocode} % Add control point $q_1$ % \begin{macrocode} \pIIe@omegai{two}% \@tempdimc\@ellipkappa\@tempdimc \@tempdimd\@ellipkappa\@tempdimd \@tempdimc-\@tempdimc \@tempdimd-\@tempdimd \advance\@tempdimc by \@tempdima \advance\@tempdimd by \@tempdimb \pIIe@add@nums\@tempdimc\@tempdimd %\typeout{ control two: (\the\@tempdimc,\the\@tempdimd)}% % \end{macrocode} % And finally add $p_2$ to the path % \begin{macrocode} \pIIe@add@CP\@tempdima\@tempdimb %\typeout{ point two: (\the\@tempdima,\the\@tempdimb)}% \pIIe@addtoGraph\pIIe@curveto@op } % \end{macrocode} % \end{macro} % % % \subsection{Normalizing elliptical arcs} % % \begin{macro}{pIIe@elliparc} % \begin{macro}{pIIe@@elliparc} % \oarg{start}\marg{$c_x$}\marg{$c_y$}\marg{a}\marg{b}\marg{$\alpha_1$}\marg{$\alpha_2$}\\ % \strut\\ % These two macros check the arguments and normalize the angles. % \begin{macrocode} \newcommand*\pIIe@elliparc[7][0]{% % \end{macrocode} % Store the radii in registers, where |\@ovro|$=a$ and |\@ovri|$=b$. % \begin{macrocode} \@ovro #4\relax \@ovri #5\relax \iffalse%dim\@ovro=\@ovri % \end{macrocode} % Call the circular arc routine if the x- and y-radius are equal % \begin{macrocode} \pIIe@arc[#1]{#2}{#3}{#4}{#6}{#7} \else % \end{macrocode} % Normalize angles such that the arc angle $\abs{\alpha_2 - \alpha_1}\,\le 720$. % Store the arc angle in |\@arclen|. % \begin{macrocode} \ifdim \@ovro<\z@ \pIIe@badcircarg\else \ifdim \@ovri<\z@ \pIIe@badcircarg\else \@arclen #7\p@ \advance\@arclen -#6\p@ \ifdim \@arclen<\z@ \def\@tempsign{-}\else\def\@tempsign{}\fi \ifdim \@tempsign\@arclen>720\p@ \PackageWarning {ellipse}{The arc angle is reduced to -720..720}% \@whiledim \@tempsign\@arclen>720\p@ \do {\advance\@arclen-\@tempsign360\p@}% \@tempdima #6\p@ \advance\@tempdima \@arclen \edef\@angleend{\strip@pt\@tempdima}% \pIIe@@elliparc{#1}{#2}{#3}{#6}{\@angleend}% \else \pIIe@@elliparc{#1}{#2}{#3}{#6}{#7}% \fi \fi \fi \fi } % \end{macrocode} % |\pIIe@@elliparc| divides the total angle in parts of at most $90$ degrees. % Assumes |\@ovro|$=a$ and |\@ovri|$=b$, and |\@arclen| the arc angle, with |\@tempsign| % sign of the arc angle. % \begin{macrocode} \newcommand*\pIIe@@elliparc[5]{% \begingroup \ifdim \@tempsign\@arclen>90\p@ % \end{macrocode} % If the arc angle is too large, the arc is recursively % divided into 2 parts until the arc angle is at most 90~degrees. % \begin{macrocode} \divide\@arclen 2% \@tempdima #4\p@\advance\@tempdima by \@arclen \edef\@anglemid{\strip@pt\@tempdima}% \def\@tempa{\pIIe@@elliparc{#1}{#2}{#3}{#4}}% \expandafter\@tempa\expandafter{\@anglemid}% \def\@tempa{\pIIe@@elliparc{2}{#2}{#3}}% \expandafter\@tempa\expandafter{\@anglemid}{#5}% \else % \end{macrocode} % The arc angle is smaller than 90 degrees. % \begin{macrocode} \pIIe@elliparc@{#1}{#2}{#3}{#4}{#5}% \fi \endgroup }% % \end{macrocode} % \end{macro} %\end{macro} % \subsection{Drawing elliptical arcs} % %\begin{macro}{\elliparc} %\begin{macro}{\pIIeelliparc} % \oarg{start}\marg{center-x}\marg{center-y}\marg{radius-x}\marg{radius-y}\marg{start-angle}\marg{end-angle}\\ % \strut\\ % The main elliptical arc drawing routine. We start with |\pIIeelliparc| to avoid conflicts with % other packages. % \begin{macrocode} \newcommand*\pIIeelliparc[7][0]{% \@killglue \pIIe@elliparc[#1]{#2\unitlength}{#3\unitlength}{#4\unitlength}{#5\unitlength}{#6}{#7}% \ignorespaces% } \ifx\undefined\elliparc\else \PackageWarning{ellipse}{\protect\elliparc\space is redefined}% \fi \let\elliparc\pIIeelliparc % \end{macrocode} % \end{macro} % \end{macro} % \begin{macro}{\earc} % \begin{macro}{\earc*} % |[|\rarg{$\alpha_0$}|,|\rarg{$\alpha_1$}|]|\marg{radius-x}\marg{radius-y}\\ % \strut\\ % The \cmd{\earc} command generalizes the standard \cmd{\arc} with both % a $x$- and $y$-radius. The |\earc*| version draws a filled elliptical arc while % |\earc| only strokes the elliptical arc. Both take an optional comma separated pair of % angles which specify the initial and final angle ($0$ and $360$ by default). % We start with \cmd{\pIIeearc} to avoid conflicts with otherpackages. % \begin{macrocode} \newcommand*\pIIeearc {\@ifstar{\@tempswatrue\pIIe@earc@}{\@tempswafalse\pIIe@earc@}} \newcommand*\pIIe@earc@[3][0,360]{\pIIe@earc@@(#1){#2}{#3}} \def\pIIe@earc@@(#1,#2)#3#4{% \if@tempswa \pIIe@moveto\z@\z@ \pIIe@elliparc{\z@}{\z@}{#3\unitlength}{#4\unitlength}{#1}{#2}% \pIIe@closepath\pIIe@fillGraph \else \pIIe@elliparc[1]{\z@}{\z@}{#3\unitlength}{#4\unitlength}{#1}{#2}% \pIIe@strokeGraph \fi} \ifx\undefined\earc\else \PackageWarning{ellipse}{\protect\earc\space is redefined}% \fi \let\earc\pIIeearc % \end{macrocode} % \end{macro} % \end{macro} % \begin{macro}{\ellipse} % \begin{macro}{\ellipse*} % \marg{radius-x}\marg{radius-y}\\ % \strut\\ % The \cmd{\ellipse} draws an ellipse with the specified $x$- and $y$-radius. % The |\ellipse*| version draws a filled ellipse. % We start with \cmd{\pIIeellipse} to avoid conflicts with other packages. % The implementation redirects immediately to |earc| which generalized this command. % \begin{macrocode} \newcommand*\pIIeellipse {\@ifstar{\@tempswatrue\pIIe@earc@}{\@tempswafalse\pIIe@earc@}} \let\ellipse\pIIeellipse % \end{macrocode} % \end{macro} % \end{macro} % % \Finale \endinput