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

Ö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
Aus: ST-Computer 03 / 1988, Seite 80

Links

Copyright-Bestimmungen: siehe Über diese Seite