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.
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.
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.
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
/*
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);
}
}