CAD-Funktionen à la Carte

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.

Bild 1: Ermittlung des Mittelpunkts und des Radiuses

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


Aus: ST-Computer 05 / 1992, Seite 76

Links

Copyright-Bestimmungen: siehe Über diese Seite