Als wir vor kurzem in einigen CAD-Programmen herumstöberten, entdeckten wir ein paar nützliche, grundsätzliche Funktionen, die wohl nicht jedem zugänglich sein dürften. Dazu zählen z.B. die Kreiskonstruktion durch drei Punkte oder die Erzeugung eines mit der Maus bestimmten Kreisausschnitts. Weiterhin entschieden wir uns dafür, die Programmierung eines geeigneten Fadenkreuzes, eines Rasters und die Einrastung in einem bestimmten Rasterabstand zu beschreiben.
Christian Roth und Matthias Brust
Für den CAD-User scheinen dies noch einfache Funktionen eines CAD-Programms zu sein. Für den Programmierer jedoch sieht die Sache gar nicht mehr so einfach aus, sogar die Mathematik hat ihre Finger im Spiel. Fangen wir zuerst mit dem Fadenkreuz an, welches natürlich so geschaffen sein soll, daß der Flintergrund nicht zerstört wird. Glücklicherweise bietet uns GEM hierzu einige nützliche Funktionen an. Grundsätzlich läßt sich sagen, daß wir ständig eine Linie parallel zur x-Achse und eine parallel zur y-Achse zeichnen müssen, die sich in der aktuellen Mausposition schneiden. Um das Koordinatenkreuz etwas abzuheben, benutzen wir einfach einen anderen Linienstil, nämlich den gepunkten. Diesen können wir ja bekanntlich mit DEFLINE beliebig definieren. Doch nun zu dem Problem, daß der Hintergrund nicht gelöscht werden darf. Dazu wechseln wir einfach den Grafikmodus mit GRAPHMODE 3 auf den XOR-Modus. In diesem Modus werden alle Punkte invertiert, die schon vor dem Zeichnen des Fadenkreuzes gesetzt wurden. Wenn nun das Koordinatenkreuz noch einmal auf die gleiche Stelle gezeichnet wird, verschwindet es, und wir haben wieder unseren Originalhintergrund. Jedoch gibt es noch eine kleine Unschönheit, denn das Fadenkreuz würde durch das ständige Neuzeichnen flimmern. Deshalb wird es erst wieder neu gesetzt, wenn sich die Mausposition verändert hat. Dies alles ist im Listing in der Prozedur kreuz verwirklicht, welche noch einen Parameter namens anz& mit auf den Weg bekommt.
Sie gibt an, wieviele Punkte mit einem Einfachklick durch das Kreuz festgelegt werden sollen. Die Speicherung der Koordinaten dieser Punkte erfolgt im Array koords&(). Benötigen Sie beispielsweise wie bei dem Kreisbeispiel drei Punkte, so schreiben Sie kreuz(2), da auch noch die Null mitzählt. Damit können wir zum Raster und der Einrastungsfunktion übergehen. Das Raster ist schnell gezeichnet. Wir benötigen nur zwei ineinander geschachtelte FOR-NEXT-Schleifen, eine für die x-Richtung und eine für die y-Richtung. Es wird jeweils ein Punkt im gewählten Abstand in beide Richtungen gezeichnet. Der Rasterabstand ist in der Variablen st& festgelegt. Den richtigen Punktabstand erreichen wir durch STEP st& in den FOR-NEXT-Schleifen. Die Prozedur, die das Raster plottet, heißt raster Sie sollten stets darauf achten, daß der Rasterabstand nicht fünf unterschreitet, denn ein solcher Abstand würde schon fast den ganzen Bildschirm schwärzen. Die nächste Schwierigkeit stellt die Einrastung dar. Da es fast unmöglich ist, dem Betriebssystem mitzuteilen, daß sich die Maus in bestimmten Abständen bewegen soll (man müßte einen neuen Maustreiber schreiben), bedienen wir uns eines kleinen Tricks. Einrasten soll ja eigentlich nur das Fadenkreuz und nicht der Mauszeiger selbst. Deshalb lassen wir mit HIDEM die Maus verschwinden und kümmern uns nur um deren Koordinaten. Aus diesen ermitteln wir einfach die Koordinaten des nächststehenden Einrasterungspunktes und zeichnen das Fadenkreuz an diese Position. Das ist auch der Grund, weshalb es am günstigsten ist, die Einrasterung in die Prozedur kreuz zu legen. Die Koordinaten des nächstliegenden Rasterpunktes lassen sich mit der folgenden Formel aus den tatsächlichen Mauskoordinaten errechnen:
x = INT(x_maus ; st& + 0.5) * st&
y = INT(y_maus ; st& + 0.5) * st&,
wobei x,y den Rasterkoordinaten und x_maus,y_maus leicht erkennbar den Mauskoordinaten entsprechen. Sie können ja einmal geeignete Werte einsetzen, um herauszufinden, wie die Formel arbeitet. Versuchen Sie es doch beispielsweise mit 320, 325, 330, 335 und einem Rasterabstand von 20 Pixeln. Damit hätten wir dieses Problem auch schon gelöst. Eines bleibt noch zu sagen. Um zu gewährleisten, daß das Betriebssystem auch die Rasterkoordinaten zurückgibt, müssen wir, wenn der Mausklick in der Prozedur kreuz erfolgt ist, die Maus mit Hilfe von SETMOUSE x,y auf den errechneten Rasterpunkt setzen. Doch jetzt zu den erwähnten Kreisgrundfunktionen. Flierzu werden wir auch etwas grundlegende Mathematik benötigen. In der ersten Funktion soll ein Kreis durch Angabe von drei Punkten konstruiert werden. Dies haben wir ausgewählt, da eine solche Funktion nur in sehr wenigen Programmen zu finden ist. Zuerst legen wir mit dem Prozeduraufruf kreuz(2) die drei Punkte fest. Die Koordinaten befinden sich wie schon oben erwähnt im Array koords&() Jetzt müssen wir irgendwie aus diesen Punkten den Mittelpunkt und den Radius des Kreises ermitteln. Dazu betrachten wir die Punkte als Eckpunkte eines Dreiecks. Unser gesuchter Kreis stellt dann den Umkreis dieses Dreiecks dar. Wie Sie in jedem guten Buch lesen können, befindet sich der Mittelpunkt des Umkreises im Schnittpunkt der drei Mittelsenkrechten. Betrachten sie hierzu die Skizze in Abbildung 1. Berechnen wir zunächst den Vektor von Punkt 1 nach Punkt 2 mit:
v = ((x2 - x1),(y2 - y1))
Um nun den Ausgangspunkt der Mittelsenkrechten zu erhalten, welcher, wie der Name schon sagt, im Mittelpunkt des Vektors v liegt, multiplizieren wir einfach die obige Formel mit 0.5. Der Vektor der Mittelsenkrechten steht nun zu diesem Vektor senkrecht und berechnet sich wie folgt:
m = ((y1 - y2),(x2 - x1))
Diese Berechnungen fertigen wir für zwei Mittelsenkrechten an, da ja damit der Schnittpunkt schon feststeht. Jetzt fehlt uns noch die Länge dieses Vektors. Hierzu gilt für den Vektor v und die Länge der Zusammenhang:
k * Xm1 + I * Xm2 = Xm1 - Xm2
k * Ym1 + I * Ym2 = Ym1 - Ym2,
wobei Xm1, Ym1 und Xm2, Ym2 die Vektorkoordinaten der zwei berechneten Vektoren v. In der Prozedur KREIS_3 ist dieses Gleichungssystem mit Hilfe einer etwas komplizierteren mathematischen Regel schon nach den Mittelpunktskoordinaten aufgelöst worden. Diese befinden sich in den Variablen Mx& und My&. Jetzt beschert uns auch der Radius keine Probleme mehr, denn wir kennen den Mittelpunkt und drei Punkte auf dem Kreis, von denen wir jedoch nur einen zur Radiusberechnung benötigen. Hier hilft uns nämlich der Satz von Pythagoras:
r = SQR((x1 - Mx)^2 + (y1 - My)^2)
Die zweite Grundfunktion ermöglicht uns die Konstruktion eines Kreisausschnitts. Zu Beginn müssen Sie mit Hilfe von kreuz(1) zwei Punkte bestimmen, und zwar den Mittelpunkt und einen Punkt auf der Kreislinie. Damit können wir mit der gleichen Formel wie oben den Radius r ermitteln. Jetzt erscheint eine Linie, mit der Sie den Anfangs- und Endwinkel angeben. Die Programmierung dieser Linie erfolgt ähnlich wie die des Koordinatenkreuzes, da auch diese Linie den Hintergrund nicht zerstören soll. Die beiden Winkel lassen sich anschließend mit dem Tangens berechnen, denn wir haben einen Punkt (das Ende der Linie) und den Mittelpunkt. Wir können nun die x- und y-Koordinaten des Vektors vom Mittelpunkt bis zum Linienendpunkt berechnen. Dividiert man nun y durch x, erhält man als Quotient den Tangens, dessen Umkehrfunktion, Arcustangens, uns den gewünschten Winkel liefert. Hiermit wäre auch dieser Fall erledigt.
' ***********************************************
' * *
' * PROGRAMM: CAD a'la Carte *
' * SPRACHE: GFA-BASIC V3.X *
‘ * AUTOREN: Christian Roth und Matthias Brust *
' * (c) 1992 MAXON Computer *
' ***********************************************
DIM koords&(5),w&(3) !Felder dimensionieren
HIDEM !Maus weg
INPUT "Bitte Rasterabstand eingeben (>5): ",st&
CLS
raster !Raster zeichnen
kreuz(2) !3 Punkte holen
kreis_3 !Kreis konstruieren
GEMSYS 20 !auf Taste warten
CLS !Bildschirm löschen
raster !neues Raster zeichnen
kreuz(1) !2 Punkte holen
kreis_ausschnitt !Kreisausschnitt konstr.
GEMSYS 20 !auf Taste warten
SHOWM !Maus wieder herholen
PROCEDURE kreis_3 !Kreis durch 3 Punkte konstr.
LOCAL x1&,x2&,x3&,y1&,y2&,y3&,xm1,ym1,xm2,ym2
LOCAL diskr,1,mx&,my&,r&,h1,h2
x1&=koords&(0) !Koordinaten der
y1&=koords&(1) !drei Punkte holen
x2&=koords&(2)
y2&=koords&(3)
x3&=koords&(4)
y3&=koords&(5)
xm1=0.5*SUB(x2&,x1&) !Vektor v
ym1=0.5*SUB(y2&,y1&) !berechnen
xm2=0.S*SUB(x3&,x1&)
ym2=0.5*SUB(y3&,y1&)
diskr=xm2*ym1-xm1*ym2 !Diskriminante
IF diskr<>0 THEN
h1=ym1-ym2
h2=xm1-xm2
l=(ym1*h1+xm1*h2)/diskr !Länge von m
mx&=xm2+x1&-l*ym2+1 !Kreismittelpunkt
my&=ym2+y1&+l*xm2+1 !ermitteln
r&=SQR(SUB(mx&,x1&)^2+SUB(my&,y1&)^2)+1
CIRCLE mx&,my&,r& !Radius ber.,Kr. zeichnen
ENDIF
RETURN
PROCEDURE kreis_ausschnitt !Ausschnitt konstr.
LOCAL x&,y&,x1&,y1&,r&
LOCAL i&,xv,yv,w1,w2
x&=koords&(0) !Koordinaten holen
y&=koords&(1) !Radius berechnen
r&=SQR(SUB(koords&(2),koords&(0))^2 + SUB(koords&(3),koords&(1))^2)
GRAPHMODE 3 !neuer Grafikmodus für Linie
CIRCLE x&,y&,r& !ganzen Kreis zeichnen
FOR i&=0 TO 1 !2 mal Linie darstellen
x1&=x& !Anfangspunkt = Mittelpunkt
y1&=y&
REPEAT
DRAW x1&,y1& TO x&,y& !Linie löschen
MOUSE x1&,y1&,k
DRAW x1&,y1& TO x&,y& !wieder zeichnen
UNTIL k=1 !Bis linke Taste gedrückt
DRAW x1&,y1& TO x&,y& !Linie löschen
w&(i&*2)=x1& !Koordinaten
w&(i&*2+1)=y1& !festhalten
REPEAT !Warten, bis linke
UNTIL MOUSEK=0 !Maustaste losgelassen
NEXT i&
xv=1.0E-12+SUB(w&(0),x&) !Vektor vom Mittelp.
yv=1.0E-12+SUB(w&(1),y&) !bis Linienendp. ber.
w1=DEG(ATN(yv/xv))*10*SGN(yv/xv) !Winkel ber.
IF xv<0 THEN
w1=1800-w1 !in richtigen Bereich bringen
ENDIF !von 0-3600
MUL w1,SGN(-yv)
ADD w1, SUB(1,SGN(w1))*ABS(SGN(INT(w1)))*1800
xv=1.0E-12+SUB(w&(2),x&) !folgendes entspr.
yv=1.0E-12+SUB(w&(3),y&) !obigen Zeilen
w2=DEG(ATN(yv/xv))*10*SGN(yv/xv)
IF xv<0 THEN
w2=1800-w2
ENDIF
MUL w2,SGN(-yv)
ADD w2,SUB(1,SGN(w2))*ABS(SGN(INT(w2)))*1800
CIRCLE x&,y&,r& !ganzen Kreis löschen
GRAPHMODE 1
DEFLINE 1,,, !normaler Linienstil
CIRCLE x&,y&,r&,w1,w2 !Kreisausschnitt zeichn.
RETURN
PROCEDURE kreuz(anz) !Fadenkreuz darstellen
LOCAL x&,y&,x_old&,y_old&,i&
GRAPHMODE 3 !Neuer Grafikmodus
DEFLINE -21845,1,, !Neuer Linienstil
x_old&=INT(MOUSEX/st&+0.5)*st&
y_old&=INT(MOUSEY/st&+0.5)*St&
FOR i&=0 TO anz !Anzahl d. Klicke
x&=x_old&
y&=y_old&
IF i&=0
LINE x&,0,x&,399
LINE 0,y&,639,y&
ENDIF
REPEAT
x_old&=x&
y_old&=y&
MOUSE x&,y&,k !Mauskoord. festhalten
x&=INT(x&/st&+0.5)*st& !Rasterp. errechnen
y&=INT(y&/st&+0.5)*st& ! " "
IF x&<>x_old& OR y&<>y_old& !Koord. gleich
LINE x_old&,0,x_old&,399 !altes Kreuz
LINE 0,y_old&,639,y_old& !löschen
LINE x&,0,x&,399 !Neues zeichnen
LINE 0,y&,639,y&
SETMOUSE x&,y&
ENDIF
UNTIL k=1 !Bis linke Taste gedr.
DEFLINE 1,0,0
LINE x&-3,y&-3,x&+3,y&+3 !kleines Kreuz
LINE x&-3,y&+3,x&+3,y&-3 !zeichnen
koords&(i&*2)=x& !Koord.
koords&(i&*2+1)=y& !speichern
DEFLINE -21845,1,0,0
REPEAT !Bis Maustaste
UNTIL MOUSEK=0 !losgelassen
NEXT i&
LINE x_old&,0,x_old&,399 !altes Kreuz
LINE 0,y_old&,639,y_old& !löschen
SETMOUSE x&,y& !Maus auf Rasterp. setzen
DEFLINE 1,0,0
GRAPHMODE 1
RETURN
PROCEDURE raster !Raster erzeugen
LOCAL i&,i1&
FOR i&=st& TO 640-st& STEP st& !x-Richtung
FOR i1&=st& TO 400-st& STEP st& !y-Richtung
PSET i&,i1&,1 !Rasterpunkt setzen
NEXT i1&
NEXT i&
RETURN