Von Zeichen, Winkeln und Rahmen: Textrahmen für Speedos rotierbare Zeichensätze

Speedo hat dem VDI zu neuem Glanz verholten. Mit seinen hochwertigen, frei skalier- und drehbaren Zeichensätzen läßt es die Textausgabe auf dem ATARI in neuem Gewand erscheinen. I ber die Veränderung von Zeichenhöhen und -breiten wurde in den letzten Monaten schon viel geschrieben, aber wie sieht es mit der freien Drehbarkeit aus? Fehlanzeige, denn viele Programme unterstützen zwar die Speedo-Zeichensätze, bieten aber nur die Drehbarkeit in 90-Grad-Schritten an.

Um in einem objektorientierten Editor arbeiten zu können, braucht jedes Objekt seinen Rahmen. Für Kreise. Rechtecke und Linien ist dies nicht weiter schwer. Sie bringen ihre maximalen Koordinaten meistens schon mit. Etwas schwieriger wird es erst mit Zeichenketten oder ganzen Textpassagen, die zudem auch noch drehbar sind. Von Vorteil wäre eine Routine, die die Rahmenkoordinaten für jeden beliebigen Winkel und alle möglichen Attribute berechnet. Das Listing enthält eine solche Routine in C (Lattice C 5.5; Da der Quellcode kurz gehalten wurde, ist die Portierung auf jeden anderen Compiler möglich, vorausgesetzt man versteht die C-Syntax. Als Grafiktreiber wurden das ROM-TOS-VDI, NVDI 2.03 und NOVA-VDI 1.68 verwendet.

Erste Lösungen

Für unser Problem bietet das VDI mit der Funktion vqt_extent() die erste Lösung an. Sie berechnet die Eckpunktkoordmaten einer beliebigen Textausgabe (siehe [1]). Leider ist sie für unsere Zwecke nur bedingt geeignet. Die mit vqt_alignment() gesetzte horizontale und vertikale Ausrichtung wird nichtbeachtet, d.h. die Routine im abgedruckten Listing Stufe 1 arbeitet nur mit links oben orientierten Texten. Es wäre möglich, die mit vqt_extent() berechneten Werte so zu bearbeiten, daß auch diese Attribute beachtet werden. Eine komplett neu geschriebene Routine ist jedoch nur unwesentlich aufwendiger, denn das VDI stellt alle Möglichkeiten zur Bestimmung der Zeichenbreite und -höhe bereit. Mit vqt_fontinfo() erhält man, wie der Name schon sagt, Zeichensatzinformationen. mit vqt_width() kann die Breite eines jeden Zeichens berechnet werden, und vqt_attributes() liefert die zuletzt gesetzten Attribute (siehe alles unter [1]). Und hier taucht schon das nächste Problem auf: Die eben genannte Funktion liefert für den Rotationswinkel Werte zurück, die auf 90 Grad gerundet wurden. Eine recht einfache Lösung stellt das Unterprogramm my_vst_rotation() dar. Es speichert einfach für jede Workstation den zuletzt gesetzten Wert und stellt ihn mit my_yqt_attributes() wieder her.

Selfmade

Die Stufe 2 benutzt die eben genannten Funktionen und ermittelt einen absoluten Textrahmen. Es wird die Zeiehenkettenhöhe und -länge berechnet, wobei die meisten verrechneten Parameter den Wert Null haben. Dann werden mit einfachen trigonometrischen Funktionen die Eckpunkte berechnet.

Eine volle Unterstützung aller Textattribute bringt aber erst die Stufe 3. Sie stellt eine universelle Routine bereit, bei der rein theoretisch der Aufhängepunkt des Objektes frei gewählt werden kann. Hier sollen nur die bekannten Textattribute unterstützt werden. Aber sie könnte auch für andere Ausgaben benutzt werden, z.B. für Rechtecke mit beliebigen Angriffspunkten. Die Ansätze zu den trigonometrischen Berechnungen soll die beigelegte Abbildung verdeutlichen.

Als Beispiel wird hier nur der Eckpunkt links oben verwendet: Die Variablen la und ha beinhalten den Längen- und Höhenversatz. Hieraus wird über den Satz des Pythagoras die Länge zur Ecke (0,1) berechnet (=1). Den Winkel al berechnet man aus dem Rotationswinkel rotr und dem Längen- und Höhenversatz. Die Werte x und y stellen die kartesischen Koordinaten für diesen Eckpunkt dar. Ich möchte wetten, daß es auch einfacher geht.

Bugs

Zum Schluß soll noch auf einen weiteren Fehler hingewiesen werden: Bei Verwendung von mehreren Texteffekten (z.B. fett und kursiv) gleichzeitig und einem Drehwinkel unterschiedlich von Null gibt die Funktion vqt_width() falsche Werte für die Zeichenbreite (cell_width) zurück. Dies könnte durch Rücksetzen des Rotationswinkels, umgangen werden.

Und noch ein kleiner Tip zum Listing: Als mathematische Bibliothek habe ich die Software-Unterstüzung gewählt. Wer keine FPU unterstützen möchte und wem die Softwareemulation zu langsam ist, dem kann folgender Tip vielleicht helfen: Wenn keine hohe Genauigkeit gefordert wird, kann man die Sinuswerte von 0 bis 90 Grad in einer Tabelle abspeichern. Aus dem Sinus läßt sich der Cosinus und aus beiden der Tangens berechnen und hieraus auch der Arcustangens abschätzen. Mit der Quadratwurzel sieht es schon etwas schlechter aus.

Literatur-Hinweise:
[1]: ATARI-Profibuch ST-STE-TT

Stufe 3 der Textrotation mit Speedo
/*
    Modifizierte vqt_extent()-Routine 
    zum Umrahmen einer beliebig gedrehten 
    VDI-Textausgabe.
    Speedo GDOS wird benötigt 
    (c)1994 by MAXON-Computer 
    Autor: Volker Hemsen 
    Compiler: Lattice C 5.5
*/

#include "aes.h"
#include "vdi.h" 
#include "math.h'
#include "stdio.h"
#include "time.h"

/*
    Die meisten VDI's runden die Textrotationswinkel 
    auf neunzigstel Grad. Bei diesen kleinen Routinen 
    wird der Rotionswinkel für jede VDI-Workstation 
    abgespeichert und bei Abfrage mit my_vqt_attibutes() 
    auch richtig ausgegeben.
*/

int text_rot[100];

void my_vst_rotation(int handle,int rot)
{
    text_rot[handle]=rot; 
    vst_rotation(handle,rot);
}

void my_vqt_attributes(int handle,int *attrib)
{
    vqt_attributes(handle,attrib); 
    attrib[2]=text_rot[handle];
}

/*
    Stufe 1: benutzt wird die VDI-eigene 
             vqt_extent()-Routine 
             arbeitet nur vernünftig bei 
             links oben ausgerichteten Texten
*/

void vqt_ext1(int handle,char *s, int x,int y,int *ext)
{
    int i,ext0,extl, sgn; 
    vqt_extent(handle,s,ext); 
    if ((text_rot[handle]==900) || (text_rot[handle]==2700)) 
        sgn=-1; 
    else 
        sgn=1; 
    ext0=ext[0]*sgn; 
    ext1=ext[1]*sgn; 
    for(i=0;i<=7;)
    {
        ext[i]=ext[i++]*sgn+x-ext0; 
        ext[i]=ext[i++]*sgn+y-ext1;
    }
}

/*
    Stufe 2: Die Eckpunkte werden selbst
             berechnet, arbeitet nur vernünftig
             bei links oder rechts oben 
             ausgerichteten Texten
*/

void vqt_ext2(int handle,char *s, int x,int y,int *ext)
{
    int d,i;
    int height,length; 
    double ext2[8]; 
    double rotr;

    int minADE,maxADE;
    int distances[5],effects[3];
    int cell_width,left_delta,right_delta;
    int attrib[10];

    vqt_fontinfo(handle,&minADE,&maxADE,distances,&d,effects); 
    length=(effects[1]+effects[2]);
    while (*s!=0)
    {
        vqt_width(handle,(int)*s,&cell_width,&left_delta,&right_delta); 
        length+=(cell_width+left_delta+right_delta+effects[0]);
        s++;
    }
    my_vqt_attributes(handle,attrib);
    height=attrib[9];
    rotr=(double)(attrib[2]*PI)/1800;
    if (attrib[3]==2) 
        length=-length; 
    ext2[0]=0; 
    ext2[1]=0;
    ext2[2]=height*sin(rotr); 
    ext2[3]=height*cos(rotr); 
    ext2[4]=ext2[2]+length*cos(rotr); 
    ext2[5]=ext2[3]-length*sin(rotr); 
    ext2[6)=ext2[4]-height*sin(rotr); 
    ext2[7]=ext2[5]-height*cos(rotr);
    for(i=0;i<7;)
    {
        ext[i]=(int)ext2[i++]+x; 
        ext[i]=(int)ext2[i++]+y;
    }
}

/*
    Zusatzroutinen für Stufe 3 
    atn(): Arcustangens wird bei nicht-
           definierten Stellen korrigiert. 
    power2(): wie pow2() nur für Integer-Werte 
              und 0" lauft auch richtig!
*/

double atn(int a,int b)
{
    if (b==0)
    {
        if (a>0)
            return ((double) PI/2); 
        else
            return ((double) -PI/2);
    }
    else
        return ( atan( (double)a / (double)b ) );
}

double power2(int a) 
{
    return((double) (a*a) );
}

/*
    Stufe 3: Die Eckpunkte werden selbst
             berechnet und alle Ausrichtungs-
             möglichkeiten richtig behandelt!
*/

void vqt_ext3(int handle,char *s, int x,int y,int *ext)
{
    int d,i;
    int height,length; 
    double l,al,rotr; 
    int la,ha;

    int minADE,maxADE;
    int distances[5],effects[3];
    int cell_width,left_delta,right_delta;
    int attrib[10];

    vqt_fontinfo(handle,&minADE,&maxADE,distances,&d,effects); 
    length=(effects[1]+effects[2]); 
    while (*s!=0)
    {
        vqt_width(handle,(int) *s,&cell_width,&left_delta,&right_delta); 
        length+=(cell_width+left_delta+right_delta+effects[0]);
        s++;
    }
    my_vqt_attributes(handle,attrib); 
    height=attrib[9]; 
    rotr=(double)(attrib[2]*PI)/1800; 
    switch(attrib[4] )
    {
        case 0: ha=distances[4]; 
                break;
        case 1: ha=distances[4]-distances[2]; 
                break;
        case 2: ha=distances[4]-distances[3];
                break; 
        case 3: ha=height; 
                break;
        case 4: ha=distances[4]+distances[1];
                break; 
        case 5: ha=0;
                break;
    }
    switch( attrib[3] )
    {
        case 0: la=0;
                break; 
        case 1: la=length/2;
                break; 
        case 2: la=length; 
                break;  
    }
    l=sqrt(power2(la)+power2(ha)); 
    al=rotr-atn( ha,la ); 
    ext[0]=(int)-(l*cos(al)); 
    ext[1]=(int)(l*sin(al)); 
    l=sqrt(power2(height-ha)+power2(la)); 
    al=(PI/2)-rotr-atn(height-ha,la); 
    ext[2]=(int) -(l*sin(al)); 
    ext[3]=(int) (l*cos(al));
    l=sqrt(power2(length-la)+power2(height-ha));
    al=rotr-atn(height-ha,length-la);
    ext[4]=(int) (l*cos(al));
    ext[5]=(int) -(l*sin(al));
    l=sqrt(power2(length-la)+power2(ha));
    al=(PI/2)-rotr-atn(ha,length-la);
    ext[6]=(int) (l*sin(al));
    ext[7]=(int) -(l*cos(al));
    for(i=0;i<=7;)
    {
        ext[i++]+=x; 
        ext[i++]+=y;
    }
}

/*
    Die Hauptroutine soll die Punktionstätigkeit 
    beweisen. Es wird ein Text in 10 Grad 
    Schritten gedreht und schwarz unterlegt.
    Dies könnte z.B. einen Curser darstellen.
    Die Attribute können im Quelltext bei 
    vst_effects() und vst_alignment() geändert 
    werden. Runde 0 dient als Leerlauf, damit 
    sich bereits die Bitmaps aller Zeichen im 
    Speedo-Cache befinden und so bei der 
    Zeitmessung Chancengleichheit gewahrt bleibt.
*/

void main(void)
{
    int handle;
    int work_in[11],work_out(57];
    int i,d,runde,pxy[4],x,y,rot,max_font;
    char font_name[32];
    int ext[10];
    long cl_time;

    handle=graf_handle(&d,&d,&d,&d); 
    for(i=1;i<10;i++) 
        work_in[i]=1; 
    work_in[0]=1; 
    work_in[10]=2;
    v_opnvwk(work_in,&handle,work_out); 
    if (handle!=0)
    {
        max_font=vst_load_fonts(handle,0)+work_out[10]; 
        pxy[0]=0;pxy[1]=0;
        pxy[2]=work_out[0];pxy[3]=work_out[1]; 
        vs_clip(handle,1,pxy); 
        vsf_interior(handle,FIS_HOLLOW); 
        vr_recfl(handle,pxy); 
        for(i=1;i<=max_font;i++)
            if (vqt_name(handle,2,font_name)==5003) 
                /* nach SWISS suchen */ 
                vst_font(handle,5003); 
        vst_point(handle,40,&d,&d,&d,&d); 
        vst_effects(handle,1); 
        vst_alignment(handle,2,5,&d,&d);

        vsf_perimeter(handle,0); 
        vsf_style(handle,8); 
        x=work_out[0]/2; y=work_out[1]/2; 
        for(runde=0;runde<=3;runde++)
        {
            cl_time=clock();
            for(rot=0;rot<3600;rot+=100)
            {
                if (runde!=0)
                {
                    vswr_mode(handle,MD_REPLACE); 
                    vsf_interior(handle,FIS_HOLLOW); 
                    v_fillarea(handle,4,ext);
                }
                my_vst_rotation(handle,rot); 
                v_gtext(handle,x,y,font_name);

                switch(runde)
                {
                    case 1: vqt_ext1(handle,font_name, x,y,ext); break; 
                    case 2: vqt_ext2(handle,font_name, x,y,ext); break; 
                    case 3: vqt_ext3(handle,font_name, x,y,ext); break;
                }

                if (runde!=0)
                {
                    vswr_mode(handle,MD_XOR); 
                    vsf_interior(handle,2); 
                    v_fillarea(handle,4,ext);
                }
            }
            printf("Stufe %d  -  %f Sekunden\n",runde,(double)(clock()-cl_time)/CLK_TCK);
        }
        printf("Ende - Drucke return\n"); 
        getchar();
        vst_unload_fonts(handle,0); 
        v_clsvwk(handle);
    }
}

Volker Hemsen
Aus: ST-Computer 09 / 1994, Seite 88

Links

Copyright-Bestimmungen: siehe Über diese Seite