emacs/code/elpa/auctex-13.2.1/circ.tex

480 lines
13 KiB
TeX

\documentclass[a4paper,twocolumn]{article}
\usepackage[german]{babel}
\usepackage[T1]{fontenc}
\usepackage[latin1]{inputenc}
\usepackage[showlabels,sections,floats,textmath,displaymath]{preview}
\newbox\chaos
\newdimen\tdim
\def\fframe{%
\tdim=\columnwidth
\advance\tdim by -2\fboxsep
\advance\tdim by -2\fboxrule
\setbox\chaos=\hbox\bgroup\begin{minipage}{\tdim}}
\def\endfframe{\end{minipage}\egroup\fbox{\box\chaos}}
\unitlength 1mm
\newcount\fives
\fives 14
\newcount\ones
\ones\fives
\multiply \ones by 5
\newsavebox{\raster}
\savebox{\raster}(\ones,\ones)
{\thicklines
\put(0,0){\line(0,1){\ones}}
\put(0,0){\line(1,0){\ones}}
\multiput(0,0)(5,0){\fives}
{\begin{picture}(0,0)
\put(5,0){\line(0,1){\ones}}
\thinlines\multiput(1,0)(1,0){4}{\line(0,1){\ones}}
\end{picture}}
\multiput(0,0)(0,5){\fives}
{\begin{picture}(0,0)
\put(0,5){\line(1,0){\ones}}
\thinlines\multiput(0,1)(0,1){4}{\line(1,0){\ones}}
\end{picture}}
}
\begin{document}
\title{Einfache Kurven auf Rastergrafiken}
\author{David Kastrup}
\maketitle
\begin{abstract}
Es sollen hier einfache Methoden vorgestellt werden, um auf einer
Rastereinheit verschiedene Kurven darzustellen. Vorgestellt werden
Zeichenalgorithmen für Linien, Kreise und Hyperbeln. Die hier
hergeleiteten Gleichungen sind auch unter dem Namen {\tt DDA}s bekannt.
\end{abstract}
\section{Einführung}
Bei den hier vorgestellten Algorithmen werden zunächst nur
Kurvenstücke betrachtet, die die folgenden Eigenschaften besitzen:
\begin{enumerate}
\item Sie lassen sich als Funktion $y = f(x)$ darstellen.
\item $y$ ist im betrachteten Bereich monoton, das heißt, entweder
durchgehend steigend oder durchgehend fallend.
\item Wenn $x$ sich um $1$ ändert, so ändert sich $y$ betragsmäßig
höchstens um $1$
($\left|\frac{\partial y}{\partial x}\right| \leq 1$).
\end{enumerate}
\section{Die gerade Linie}
Wir betrachten hier zunächst nur die gerade Linie im ersten Oktanten,
die durch den Punkt $0 \choose 0$ geht. Alle anderen Linien lassen
sich durch Vertauschen von $x$ und~$y$ sowie Vorzeichenwechsel
erzeugen. Im ersten Oktanten gilt $x \geq y \geq 0$. Zum Zeichnen
einer Linie genügt es also, $x$ durchlaufen zu lassen und für $y$ die
dazugehörigen Werte zu berechnen und zu runden.
Die Gleichung einer Geraden durch $\Delta x \choose \Delta y$ lautet:
\begin{equation}
\label{lgi}
y = \frac{\Delta y}{\Delta x}x
\end{equation}
%
Nun stellen wir $y$ als Summe eines ganzzahligen Wertes $e$ und eines
gebrochenen Wertes $f$ dar, für den gilt: $-0.5 \leq f < 0.5$. Somit
stellt dann $e$ den gewünschten, auf die nächste ganze Zahl gerundeten
$y$-Wert dar. Jetzt formen wir (\ref{lgi}) um:
\begin{eqnarray}
e + f &=& x \frac{\Delta y}{\Delta x}\nonumber\\
e \Delta x + f \Delta x &=& x \Delta y\nonumber\\
f \Delta x - \left\lceil\frac{\Delta x}2\right\rceil &=&
x \Delta y - e \Delta x - \left\lceil\frac{\Delta x}2\right\rceil \label{lgii}
\end{eqnarray}
%
Den linken Ausdruck in (\ref{lgii}) bezeichnen wir jetzt mit $d$. Für
positive gerade Werte von $\Delta x$ ist offensichtlich $d < 0$ eine
zu~$f < 0.5$ equivalente Bedingung.
Für ungerade Werte von~$\Delta x$ ist $f < 0.5$ equivalent zu
$d + 0.5 < 0$.
Da $d$ stets eine ganze Zahl ist, ist dies wieder zu $d < 0$
equivalent.
% INTENTIONAL ERRORS! INTENTIONAL ERRORS! INTENTIONAL ERRORS!
%
% The following line should flag a PostScript error when previewing,
% but processing of other previews should continue.
%
Wird nun $\ifPreview\special{ps: junk}\fi f \geq 0.5$, wie sich durch
den Vergleich $d \stackrel{?}{<} 0$ feststellen läßt, so muß man
korrigieren, indem man $f$ um~1 erniedrigt und $e$ um~$1$ erhöht.
%
% The following line will make Ghostscript abort unexpectedly when
% previewing, but processing of other previews should continue.
%
$\ifPreview\special{ps: quit}\fi d$ muß dann auch entsprechend
angepaßt werden.
Mit den angegebenen Formeln ergibt sich jetzt bei Berücksichtigung der
Einflüsse von $x$ und $e$ auf $d$ der in Tabelle~\ref{linalg}
angegebene Algorithmus. Eine optimierte C-function, die die
Oktantenaufteilung berücksichtigt, ist in Tabelle~\ref{linc} zu
finden. Einige hiermit gezeichnete Linien sind in
Abbildung~\ref{linpict} zu sehen.
\begin{table}
\caption{Linienzugalgorithmus} \label{linalg}
\begin{fframe}
\begin{enumerate}
\item Setze $x \gets 0$, $y \gets 0$, $d \gets
-\left\lceil\frac{\Delta x}2\right\rceil$
\item Wiederhole bis $x = \Delta x$
\begin{enumerate}
\item Zeichne Punkt an $x \choose y$
\item Setze $x \gets x + 1$, $d \gets d + \Delta y$
\item Falls $d \geq 0$
\begin{enumerate}
\item Setze $d \gets d - \Delta x$
\item Setze $y \gets y + 1$
\end{enumerate}
\end{enumerate}
\end{enumerate}
\end{fframe}
\end{table}
\begin{table}
\caption{Linienziehen in C} \label{linc}
\begin{fframe}
\small
\begin{verbatim}
extern int x,y;
/* (x,y) ist Koordinate des nicht
* gezeichneten Startpunktes, zeigt
* nachher auf gezeichneten Endpunkt
*/
#define doline(dx,dy,advx,advy) { \
d = -(((i = dx) + 1) >> 1); \
while (i--) { \
advx; \
if ((d += dy) >= 0) { \
d -= dx; advy; \
} \
dot(x,y); \
} \
return; \
} /* Grundalgorithmus 1. Oktant */
/* dx ist Distanz in unabh. Richtung, *
* dy in abh. Richtung, advx geht *
* in unabh. Richtung, advy in abh. */
#define docond(cond,advx,advy) { \
if (cond) doline(dx,dy,advx,advy) \
doline(dy,dx,advy,advx) \
} /* Grundalgorithmus 1./2. Oktant */
/* cond ist true falls |dx| > |dy| */
void
linedraw(int dx, int dy)
/* Von (x,y) nach (x+dx, y+dx). */
{
int i;
if (dx >= 0) {
if (dy >= 0)
docond(dx > dy, ++x, ++y)
docond(dx > (dy = -dy), ++x, --y)
}
if (dy >= 0)
docond((dx = -dx) > dy,--x,++y)
docond((dx = -dx) > (dy = -dy),
--x, --y )
}
\end{verbatim}
\end{fframe}
\end{table}
\begin{figure}
\begin{picture}(\ones,\ones) \put(0,0){\usebox{\raster}}
\newcount\x
\newcount\y
\newcount\d
\newcount\dx
\newcount\dy
\x 0
\y 0
\dx \ones
\dy \ones
\loop %{
\d -\dx
\divide \d by 2 %}
\ifnum \dy > 0 %{
{\loop %{
\put(\x,\y){\circle*{1}}%}
\ifnum \x < \ones %{
\advance \x by 1
\advance \d by \dy %}
\ifnum \d > -1 %{
\advance \y by 1
\advance \d by -\dx %}
\fi %}}
\repeat}
\advance \x by 5
\advance \dx by -5
\advance \dy by -15 %}
\repeat
\end{picture}
\caption{Einige Linien}\label{linpict}
\end{figure}
\section{Der Kreis}
Wir betrachten hier nur den Achtelkreis im zweiten Oktanten
($y \geq x \geq 0$). Hier gelten die oben angegebenen Beziehungen.
Alle anderen Achtelkreise lassen sich durch elementare Spiegelungen
erzeugen.
Die Gleichung eines Kreises ist hier
\begin{equation}
y = ±\sqrt{r^2 - x^2}
\end{equation}
Der Wert $y$ läßt sich darstellen als Summe einer ganzen Zahl $e$ und
einem Wert $f$ mit $-0.5 \leq f < 0.5$. Der Wertebereich von $f$ ist
so gewählt worden, damit $e$ einen auf ganze Zahlen gerundeten Wert
für $y$ darstellt.
Nun gilt:
\begin{eqnarray}
e + f&=&\sqrt{r^2 - x^2}\nonumber\\
\label{ggg}e^2 + 2ef + f^2&=&r^2 - x^2
\end{eqnarray}
%
Die Gleichung (\ref{ggg}) hat für $x+1$ folgende Form:
\begin{eqnarray}
\label{hhh}e'^2 + 2e'f' + f'^2&=&r^2 - x^2 - 2x -1
\end{eqnarray}
%
Zieht man die Gleichung (\ref{ggg}) von (\ref{hhh}) ab, so ergibt sich
nach Umsortieren:
\begin{eqnarray*}
e' = e:\\
2e'f' + f'^2&=&2ef+f^2-2x-1\\
e' = e-1:\\
2e'f' + f'^2&=&2ef+f^2+2e-2x-2
\end{eqnarray*}
%
Jetzt wird $2ef + f^2$ mit $m$ getauft. Also:
\begin{eqnarray*}
e' = e:\\
m'&=&m -2x-1\\
e' = e-1:\\
m'&=&m +2e-1 -2x-1
\end{eqnarray*}
Wie groß ist jetzt $m$? Für $x=0$ ist es sicher $0$. Solange $e$
konstant bleibt, schrumpft $f$ stetig. Fällt $f$ unter $-0.5$, so
fällt $m$ (unter Vernachlässigung von $f^2$) unter $-e$. Dies wird
jetzt als Kriterium für einen Unterlauf von $f$ verwendet. Tritt
dieser auf, so muß $f$ um $1$ erhöht und $e$ um $1$ erniedrigt werden.
Um die Abfragebedingung zu vereinfachen, setzt man jetzt $q$ = $m+e$.
Der resultierende Algorithmus ist in Tabelle \ref{alg}, ein
optimiertes C-Programm ist in Tabelle \ref{prog} zu finden.
\begin{table}
\caption{Kreiszeichenalgorithmus}\label{alg}
\begin{fframe}
\begin{enumerate}
\item Setze $x\gets 0$, $y\gets r$, $q\gets r$
\item Wiederhole bis $x>y$:
\begin{enumerate}
\item Setze einen Punkt an $x \choose y$.
\item Setze $q\gets q-2x-1$
\item Falls $q<0$
\begin{enumerate}
\item Setze $q\gets q + 2y-2$
\item Setze $y\gets y-1$
\end{enumerate}
\item Setze $x\gets x+1$
\end{enumerate}
\end{enumerate}
\end{fframe}
\end{table}
\begin{table}
\caption{Kreiszeichenprogramm}\label{prog}
\begin{fframe}
\small
\begin{verbatim}
void
fourfold(int x0, int y0, int x, int y)
/* Zeichne in Oktant 1,3,5,7 */
/* Wird benutzt, um Anfangs- und End- *
* Punkte nicht zweimal zu zeichnen */
{
dot(x0+x,y0+y);
dot(x0-y,y0+x);
dot(x0-x,y0-y);
dot(x0+y,y0-x);
}
void
eightfold(int x0, int y0, int x, int y)
/* Zeichne in allen Quadranten */
{
fourfold(x0,y0,x,y); /* 1357 */
fourfold(x0,y0,x,-y); /* 8642 */
}
void
circle(int x0, int y0, int r)
{
fourfold(x0,y0,0,r);
/* Die ersten vier Punkte */
for (x=0, y=q=r;; ) {
if ((q -= 2* x++ + 1) < 0)
q += 2* --y;
if (x >= y)
break;
eightfold(x0,y0,x,y);
}
if (x==y)
fourfold(x0,y0,x,y);
/* Eventuell die letzten vier */
}
\end{verbatim}
\end{fframe}
\end{table}
\begin{figure}
\begin{picture}(\ones,\ones)
\put(0,0){\usebox{\raster}}
\newcount\x
\newcount\y
\newcount\q
\loop
{\x 0
\y \ones
\q \ones
\loop
\put(\x,\y){\circle*{1}}
\put(\y,\x){\circle*{1}}
\advance \q by -\x
\advance \x by 1
\advance \q by -\x
\ifnum \x < \y
\ifnum \q < 0
\advance \y by -1
\advance \q by \y
\advance \q by \y
\fi
\repeat}
\advance \ones by -10
\ifnum \ones > 0
\repeat
\end{picture}
\caption{Viertelkreise}\label{zeich}
\end{figure}
\section{Einfache Hyperbeln}
Als letztes Beispiel betrachten wir hier Hyperbeln, die der Formel
$y = r^2\!/x$ genügen, und zwar im Bereich~$x \geq r$.
Mit dem Ansatz $y = e + f$ ergibt sich:
\begin{eqnarray}
e+f &=& r^2\!/x\nonumber\\
ex + fx &=& r^2\nonumber\\
fx &=& r^2 - ex\label{phyp}
\end{eqnarray}
\pagebreak[2]
Für $x' = x+1$ hat nun (\ref{phyp}) die Form
\begin{eqnarray*}
e' = e:\\
f'x' &=& r^2 - ex - e\\
e' = e - 1:\\
f'x' &=& r^2 - ex - e + x + 1
\end{eqnarray*}
Setzt man jetzt $d = (2f + 1)x$, so ist $f < -0.5$ mit~$d < 0$
equivalent. Erreicht man diesen Fall unter Verwendung der Annahme
$e' = e$,
dann muß in bekannter Weise $f$ um~$1$ erhöht und $e$ um~$1$
vermindert werden.
\pagebreak
Für $d'$ ergeben sich dann die folgenden Werte:
\begin{eqnarray*}
e' = e:\\
d' &=& d - 2e + 1\\
e' = e - 1:\\
d' &=& d - 2e + 2x + 2 + 1
\end{eqnarray*}
Daraus ergibt sich der in Tabelle~\ref{halg} angegebene
Hyperbelalgorithmus für den ersten Oktanten.
\begin{table}
\caption{Hyperbelalgorithmus}\label{halg}
\begin{fframe}
\begin{enumerate}
\item Setze $d \gets r$, $x \gets r$, $y \gets r$
\item Wiederhole bis zufrieden
\begin{enumerate}
\item Setze Punkt an $x \choose y$
\item Setze $x \gets x + 1$
\item Setze $d \gets d - 2y + 1$
\item Falls $d < 0$
\begin{enumerate}
\item Setze $d \gets d + 2x$
\item Setze $y \gets y - 1$
\end{enumerate}
\end{enumerate}
\end{enumerate}
\end{fframe}
\end{table}
\begin{table}
\caption{Hyperbeln in C}
\begin{fframe}
\small
\begin{verbatim}
void
four(int x0, int y0, int x, int y)
/* Hyperbeln sind nur in 4 Oktanten */
{
dot(x0+x,y0+y);
dot(x0+y,y0+x);
dot(x0-x,y0-y);
dot(x0-y,y0-x);
}
void
hyperb(int x0, int y0, int r, int dx)
{
int d, x, y;
for (x = y = d = r; dx--;) {
four(x0,y0,x,y);
++x;
if ((d -= 2*y + 1) < 0) {
d += 2*x;
--y;
}
}
}
\end{verbatim}
\end{fframe}
\end{table}
\begin{figure}
\begin{picture}(\ones,\ones)
\put(0,0){\usebox{\raster}}
\newcount\x
\newcount\y
\newcount\q
\newcount\r
\r\ones
\loop
\advance \r by -10
\ifnum \r > 0
{\x \r
\y \r
\q \r
\loop
\put(\x,\y){\circle*{1}}
\put(\y,\x){\circle*{1}}
\ifnum \x < \ones
\advance \x by 1
\advance \q by -\y
\advance \q by -\y
\advance \q by 1
\ifnum \q < 0
\advance \q by \x
\advance \q by \x
\advance \y by -1
\fi
\repeat}
\repeat
\end{picture}
\caption{Hyperbeln}\label{hzeich}
\end{figure}
\end{document}