← ST-Computer 03 / 1988

Es ist ein Kreuz mit... - Skalierung leicht erzeugt

Programmierpraxis

Öfters werden Achsenkreuze für diverse Plots gebraucht. Ein kleines Unterprogramm in OMIKRON-Basic geschrieben bietet eine universelle Hilfe beim Programmieren solcher Skalierungen. Es sollte ohne größere Mühen in jedes andere Basic und mit einigen Mühen auch in andere Programmiersprachen übertragbar sein. Zwei Unterroutinen werden dazu zusätzlich benötigt:

Function Blanksweg$: : Beseitigt alle Blanks vor und nach einem String

Function Wandle$: : Erzeugt einen String mit gegebener Stellenanzahl vor und nach dem Komma.

Procedure Achse: : Eigentliche Skalierungsroutine einer Achse Procedure Achskreuz:

Anwendung für ein Achsenkreuz Das Hauptprogramm zu Beginn zeigt einige Demoaufrufe.

Wichtig bei einer Skalierung ist, daß der Aufruf unabhängig für x und y erfolgen kann und die Unterscheidung erst in der Routine erfolgt. Man muß nur die Pixelkoordinaten ‘Von’ und ‘Bis’ in der Ordinate eingeben, die man benutzen will. Der Rest müßte aus den Kommentaren hervorgehen.

Im wesentlichen zur Problemlösung folgendes:

Unt! und Ob! sind die wahren x/y-Werte unten und oben, die nicht verändert werden, damit man nachher z.B. in ein solches Achsenkreuz Funktionswerte einzeichnen kann. Eine Skalierung muß nun die Achsenwerte so auftragen, daß sie in der üblichen ‘Norm’ bleiben, d.h. also Reihenfolge 1,2,3,4.. am Zahlenanfang bzw. 10, 15,20,25 oderO, 2,4,6,8... oder 25,50,75 usw., ohne aber die untere und obere Grenze zu ändern. Für einwandfreie Auswahl der unteren und oberen Grenze ist also der Benutzer verantwortlich.

Um das einwandfrei machen zu können, wird zuerst die Dimension der Größen gecheckt und die längste mögliche Stellenanzahl ermittelt, nach der sich die Skalierung richten muß, damit es keine krummen Zahlenwerte oder/und übereinandergeschriebene Werte gibt. Dazu wird die hochauflösende Norm mit 8* 16-Pixelbuchstaben benutzt! Anschließend wird die Teilungsanzahl (die auf der 10er-Potenz beruht) in Einzelschritten solange geändert, bis eine sinnvolle und ausreichende Anzahl erreicht ist, so daß die Werte auf der Skala in ausreichendem Abstand zueinander stehen. Die feine Teilung erfolgt dann in 10* feineren Schritten (Huepf! im Ggs. zu Teil!).

Für die eigentliche Plotroutine (For P!= usw.) habe ich zur Geschwindigkeitssteigerung Hilfsgrößen vereinbart, da sie sonst doch arg langsam wird. Wichtig bei der Plotroutine ist, daß die Werte, die auf die kleinste Skaleneinheit normiert wurden (teilbar ohne Rest...), sich aber immer auf die unterste WIRKLICHE Grenze (Unt!) beziehen, da ansonsten die Skala schön, aber nicht richtig wäre!. Im Programm prüft man dann auf Teilbarkeit ohne Rest auf die großen Distanzen für die Werteschreibung.

Das Problem am ganzen Programm ist. daß mit Benutzung des Logarithmus immer kleinste Rechenreste hinzukommen. Daher darf man auch nicht auf Modulrest 0 genau prüfen, sondern muß immer auf <= einem absoluten Minimalbetrag checken, oder die Werte entsprechend vorher verringern oder vergrößern - um einen kleinen Minimalbetrag, der immer kleiner als 1 Promille bleiben muß (Auflösung des Schirms). Dieses sind die kleinen 1E-5, die hier und dort im Programm auftauchen. Ansonsten kommt es dann und wann zu unerwünschten Skalierungen, die nicht 'schön' sind. Das Programm in dieser Form ist durch Anklicken auf OM-BAS-R.PRGj lauffähig und die Routine aus dem LST-File nach Löschen des Hauptprogrammes und entsprechendem Umnumerieren mergefähig. Wichtig ist, daß Achse die Funktionen Blanksweg$ und WandleSi benötigt, die es aufruft. Mehr zur Funktionsweise zu sagen, würde Bände füllen. Ein wenig Vertiefung in das eigentlich gut kommentierte Programm dürfte auch Klarheit schaffen.

' --> Demoprogramm für die Skalen (hohe Auflösung) CLS ' Schirm löschen TEXT 50,28,"Demo reines Achsenkreuz mit einfachem Raster" ' 1.Demo Achskreuz(50,590,370,70,-30,80,20,60,0,1) ' Einfaches Achskreuz WAIT *10 ' 10 Sekunden zeigen CLS ' Schirm löschen TEXT 50,28,"Demo Achsenkreuzkasten mit doppeltem Raster" ' 2.Demo Achskreuz(50,590,370,70,-30,80,20,60,1,2) ' Einfaches Achskreuz WAIT 10 ' 10 Sekunden zeigen CLS ' Schirm löschen TEXT 50,28,"Demo für: verschiedene x-Skalierungen bei gleicher Länge" ' 3.Demo FOR I=40 TO 370 STEP 30 ' Skalenschleife x Achse(40,600,I,I,0,(I/190)^5.3,-2,0) ' Skala in x NEXT I WAIT 10 ' 10. Sekunden zeigen CLS ' Schirm löschen TEXT 50,28,"Demo für verschiedene y-Skalierungen bei gleicher Länge" ' 4.Demo FOR 1=70 TO 620 STEP 50 ' Skalenschleife y Achse(50,370,I,I,0, (I/177)^4.3,2,0) ' Skala in y NEXT I WAIT 10 ' 10 Sekunden zeigen CLS ' Schirm löschen TEXT 50,28,"Demo für verschiedene Skalierung und Raster" ' 5. Demo Achse(60,580,366,60,-30,30,-2,2) ' x-Achse unren Achse(60, 580, 60, 366,1210,1250,-1,0) ' x-Achse eben Achse(60,366,60,580,-.05,01,2,3) ' y-Achse links Achse(60,366,580,60,35,40,1,0) ' y-Achse rechts WAIT 10 ' 10 Sekunden zeigen CLS ' Schirm löschen TEXT 50,28,"Demo für gleiche Skalierung und Richtungsumkehr mit Mischraster" Achse(550,60,360,60,-20,60,-2,3) ' x-Achse unten Achse(60,550,60,360,-20,60,-1,2) ' x-Achse eben Achse(360,60,60,550,-.4,.1,2,4) ' y-Achse lunxs Achse(60,360,550,60,4,.1,1,3) ' y-Achse rechts WAIT 60 ' 10 Sekunden zeigen END ' ##################### ' # Achsenkreuzkasten # ' ##################### DEF PROC Achskreuz (Links,Rechts, Unten, Oben, Xmin!, Xmax!,Ymin!,Ymax i,Wie,Raster) ' Links - Pixelwert linke Kante Rechts - Pixelwert rechte Kante ' Unten - Pixelwert untere Kante Oben - Pixelwert obere Kante ' Xmini, Xmax! - Skalenwerte x ' Ymin!, Ymax! - Skalenwerte y ' Wie: 0 Nur Achsenkreuz, 1 Achskasten ' Raster: Rasterbreite in 4er-Pixel (0 kein Raster) Achse(Links,Rechts,Unten,Oben, Xmin!,Xmax!, -2,Raster) IF Wie=1 THEN Achse(Links,Rechts,Iben,Unten,Xmin!, Xmax!,-1,0) Achse(Unten,Oben,Links,Rechts, Ymin!, Ymax!, 2, Raster) IF Wie=1 THEN Achse (Unten, Oben, Rechts, Links, Ymin!, Ymax!,1,0) RETURN ' ##################### ' # Skalierung Achsen # ' ##################### DEF PROC Achse(Von,Bis,Wo,Hier,Unt!,Ob!,Was,Rast) ' Von - Startpunkt der Achse, Bis - Zielpunkt der Achse I ' Wo - Andere Ordinate Achse selbst, Hier - Andere Ordinate für Raster ' Unt! - Realer unterster Wert, Ob! - Realer oberster Wert ' Was - Ausrichtung (>0 y-Achse, <0 x-Achse, 1 dann rechts, 2 links, ' -2 unterhalb und -1 oberhalb der Achse) ' Rast - Raster zeichnen. (0 nein, x ja im Abstand 4er-Punkte) ' Umkehr der Werterichtung: Bis und Von vertauschen; LOCAL Unten_Dim,Oben_pim,Stellen,P!,Da,K,Huepf!, Wieoft,St ' Lokale Variablen LOCAL Stel, Dist!, Teil! ,Teil_Dim,Teil,Unten!,Oben!, Teilmax ' Lokale Variablen LOCAL Hilf1,Hilf1!,Hilf2,Hilf3,Hilf4,Hilf5,Hilf6, Merken ' Zur Beschleunigung Rast= ABS(Rast) ' Nur positives Raster Dist!= ABS(Ob!-Unt!) ' Differenz Skalenenden Ob!=Ob!+Dist!*1E-5 ' Oben bi_chen mehr Unt!=Unt!-Dist!*1E-5 ' Unten bi_chen weniger Unten!=Unt!:Oben!=Ob!' Skalenenden Unten_Dim= LOG(10, ABS(Unten!)+1E-5) ' Dimension untere Grenze Oben_Dim= LOG(10, ABS(Oben!)+1E-5) ' Dimension obere Grenze St= MAX (Oben_Dim, Unten_Dim) ' Optimale Stellenanzahl ' —-- Ermittlung der maximalen Stellenzahl Stel= ABS(St)+2-2*(St<=1) - (Un! <0) ' Maximale Stellenzahl merken IF Was>0 THEN ' y-Achse DRAW Wo,Von TO Wo,Bis ' y-Achse zeichnen Hoehe= ABS(Bis-Von) ' Pixelhöhe der Achse Teilmax= INT(Hoehe/22) ' max. Zahleinteilung ELSE ' x-Achse DRAW Von,Wo TO Bis,Wo ' x-Achse zeichnen Breite= ABS(Bis-Von) ' Pixellänge der Achse Teilmax= INT(Breite/10/Stel) ' max. Zahleinteilung ENDIF ' — Ermittlung der groben Teilung (Zahlenwerte) Stellen=St ' Zuweisung der Stellen REPEAT ' Bei engen Werten solange... Stellen=Stellen-1 ' ... erniedrigen, bis ... Huepf!=10^Stellen ' ... Schrittweite OK. Oben!=( INT(Ob!/Huepf!))*(Huepf!) ' Grenze oben drunter setzen Unten!*( INT(Unt!/Huepf!)+1)* (Huepf!)' Grenze unten drüber setzen UNTIL ABS((Oben!-Unten!)/(Huepf!))>Teilmax ' Feinstriche md. sooft wie Teilung Teil!=Huepf!*10 ' Variable grobe Teilung Teil_Dim= INT ( LOG(10,Teil!)) ' Dimension des Teils Teil=(Oben!-Unten!)/Teil! ' Ganzanteil Merken=0 ' Merkvariable für .25 Teilung IF(Teil*5)<=Teilmax THEN Teil!=Teil!/5:Teil=Teil*5 ' Skalenteile zu wenig IF(Teil*2)<=Teilmax THEN Teil!=Teil!/2:Teil*Teil*2 ' Skalenteile zu wenig IF(Teil*5)<=Teilmax THEN Teil!=Teil!/5:Teil*Teil*5 ' Immer noch zu eng IF(Teil*2)<=Teilmax THEN Teil!=Teil!/2:Teil=Teil*2: Merken=1 ' ,25-Teilung! ' — Ermittlung der feinen Teilung (Striche) Huepf!=Teil!/10 Oben!=( INT(Ob!/Huepf!))*(Huepf!)' Grenze oben drunter setzen Unten!=( INT (Unt! /Huepf!) +1) * (Huepf!) ' Grenze unten drüber setzen Dist!=Oben!-Unten! ' Differenz Skalenenden Wieoft=Dist!/Huepf! ' ..neue Anzahl der Schritte Vorher=Stel+2 ' Stellen vor dem Komma Grenze!=Huepf!/10 ' Limitdefinition Hilf1!= INT ( LOG(10,Teil!)+1D-8)' Hilfsgrö_e Nachher=MAX(0,-Hilf1!)+Merken ' Nachkommastellen Hilf1! =(Bis-Von)/(Ob!-Unt!) ' 1. Hilfsgrö_e zur Beschleunigung Hilf1=(1.5-Was)*4' 2. Hilfsgrö_e zur Beschleunigung Hilf2=Rast*3* SGN(Hier-Wo) ' 3. Hilfsgrö_e zur Beschleunigung Hilf3=(1.5+Was)*4’ 4. Hilfsgrö__e zur Beschleunigung Hilf4=10*(Was=1):Hilf5=8*(Was=2) ' 5.+6. Hilfsgrö_e Hilf6=-24*(Was=-2)+9*(Was=-1) ' 7. Hilfsgrö_e FOR P!=Unten! TO Oben!+Dist!*1E-5 STEP Huepf! ' Schleife P$=FN Wandle$(P!»Vorher,Nachher)' String bilden P$=FN Blanksweg$(P$) ' Blanks weg Da=Von+(P!-Unt!)*Hilfl!' Ort ermitteln(wahrer Ort!) Pmodul!= ABS ( INT((P!+Grenze!)/Teil!)*Teil!-P!) ' Wertentscheidungsvariable IF Was>0 THEN ' y-Achse DRAW Wo,Da TO Wo+Hilf1,Da ' Zeichnen des feinen Striches IF Pmodul!<Grenze! THEN ' Platz für Text DRAW Wo,Da TO Wo+Hilf1*2.5,Da ' Grober Strich TEXT Wo-Hilf4+(1+ LEN(P$))*Hilf5,Da+7,P$ ' Zahl schreiben IF Rast>0 THEN FOR K=Wo TO Hier STEP Hilf2: DRAW K,Da: NEXT K ' Raster ENDIF ENDIF ELSE ' x-Achse DRAW Da,Wo TO Da,Wo-Hilf3 ' zeichnen des feinen Striches IF Pmodul!<Grenze! THEN ' Platz für Text DRAW Da,Wo TO Da,Wo-Hilf3*2.5 ' Grober Strich TEXT Da- LEN(P$)*4,Wo+Hilf6, P$ ' Zahl schreiben IF Rast>0 THEN FOR K=Wo TO Hier STEP Hilf2: DRAW Da,K: NEXT K ' Raster ENDIF ENDIF ENDIF NEXT P! RETURN ' ################################## ' # Löscht Blanks vorne und hinten # ' ################################## DEF FN Blanksweg$(Was$) LOCAL Vorne,Hinten' Lokale Variablen FOR Vorne=1 TO LEN(Was$)' 1. Buchstaben ungleich Blank IF MID$(Was$,Vorne, 1)<>" " THEN EXIT NEXT Vorne FOR Hinten= LEN(Was$) TO 1 STEP -1 ' Letzten Buchstaben ungleich IF MID$(Was$,Hinten,1)<>" " THEN EXIT NEXT Hinten IF Vorne<1 THEN Vorne=1 ' Probleme korrigieren IF(Hinten-Vorne)<1 THEN Hinten=Vorne+1 ' Bei Problemen korrigieren RETURN MID$(Was$,Vorne,Hinten-Vorne+1)' Blankfrei zurückgeben ' ########################################## ' # String bilden mit Vor und Nach-Stellen # ' ########################################## DEF FN Wandle$(Wert!,Vor,Nach) LOCAL Total,Wie$,Wxu$ ' Lokale Variablen IF Nach<0 THEN Nach=0 ' Nach Minimum 0 IF Vor<1 THEN Vor=1 ' Vor Minimum 1 Total=Vor+Nach+1+(Nach=0)' Totallänge Wie$="#"*Vor' Vorstellen IF Nach>0 THEN Wie$*Wie$+"."+"#"*Nach'Nachstellen USING Wie$' So gebrauchen Wxu$= STR$(Wert!)' Wert zuweisen USING ' Wieder Normalgebrauch RETURN Wxu$' und Wert zurück..
Jost Jahn