Sinuskurve berechnen

Alles, was nicht direkt mit Python-Problemen zu tun hat. Dies ist auch der perfekte Platz für Jobangebote.
The Hit-Man
User
Beiträge: 435
Registriert: Montag 20. Februar 2006, 18:11
Wohnort: Menden / Sauerland
Kontaktdaten:

Ich bin gerade dabei unter SDL ein Oldschool-Demo zu schreiben. Vielleicht kennt jemand von Euch noch den alten C64er. Ich würde gerne einen Tic-Tac Effekt programmieren. Aus den C64er Zeiten hatte ich immer ein Programm mit dem ich Sinuswerte errechnen lassen konnte ( das Schwingen des Logos, damit es weich hin und her schwingt ). Da nen PC ja weit aus schneller ist, muß ich diese Werte ja nicht mehr vorberechnen, sondern könnte es RealTime berechnen lassen. Nun stehe ich da und weiß nicht mehr genau wie man eine solche Sinuskurve berechnen kann denn die Kurve muß ja auch wieder geschlossen werden. Kann mir da jemand auf die Sprünge helfen?
deets

Na, du musst doch einfach nur Vielfache von 2*pi nehmen, und das wiederum auf die Zeilen verteilen.
The Hit-Man
User
Beiträge: 435
Registriert: Montag 20. Februar 2006, 18:11
Wohnort: Menden / Sauerland
Kontaktdaten:

so ganz verstehe ich es noch nicht. der Bereich soll sich von 0 bis 400 erstrecken. das würde ja dann heißen:

Code: Alles auswählen

math.pi*2*Strecke ( Wert zwischen 0 und 400 )
Wenn ich nun 0 immer um 1 erhöhe würde das Logo ja nur nach rechts schwingen. ich müßte also wenn >400 den Wert wieder -1 nehmen um wieder auf 0 zu kommen oder dir Kurve einfach inventieren?
deets

sinus geht von +1 bis -1. Das heisst wenn du 400 Pixel horizontalen Versatz haben willst (was ich etwas viel finde), dann multiplizierst du nur mit 200.

Und du musst halt die Schrittweite pro Zeile berechnen, das muessten (f * 2 * pi / zeilenanzahl) sein. So grob also (aus dem Kopp):

Code: Alles auswählen

f = 4 # 4 schwingungen
w = 200
h = 400
step = f * 2 * pi / h
for i in xrange(h):
     shift = sin(i * step) * w
     ...

Mir ist noch nicht so ganz klar, was genau du eigentlich fuer einen Effekt meinst (ich hab' zwar auch damals Sine-Scroller & Co auf dem Amiga gebaut, aber den Effeknamen kenne ich nicht).
The Hit-Man
User
Beiträge: 435
Registriert: Montag 20. Februar 2006, 18:11
Wohnort: Menden / Sauerland
Kontaktdaten:

Ähm der Effekt heißt TicTac. Aus dem Grund weil das Logo wie ein Uhrpendel von rechts nach links schwingt und das eben weich per Sinus. Es gab auch noch diesen TechTech Effekt, mit dem man das Logo, wie eine Art Wasser ( Welle ) schwingen lassen konnte. Alle Effekte kenne ich auch nicht mehr. Auf dem Amiga hatte ich mal nen kleinen Scrollen in Assembler programmiert. Ich war damals eher auf dem C64er aktiv. Unter dem Amiga haben mir die Bitplanes nicht so gefallen. Werde Deine Routine mal testen. Es gab auch noch andere Sinusberechnungen, zum Beispiel, das das Logo rechts gegen den Bildschirmrand schlug und dann soft wieder nach links scrollte. Ist nen bischen schwer zu beschreiben. Allerdings dieses C64er Programm, mit dem man die Werte vorberechnen konnte, hatte eine Unmenge an Möglichkeiten um sich Koordinaten berechnen zu lassen.
deets

Du willst also ein Logo einfach nur hin und herschwingen lassen - ohne das Logo selbst zu verformen? Dann reicht doch einfach nur

t * f * 2 * pi * width

wobei t die Zeit ist in Sekunden (time.time() zB), f wie gehabt die Schwingungsfrequenz, und width die Breite des schwingens. natuerlich musst du dann noch alles in die Mittelposition verschieben.
BlackJack

@The Hit-Man: Das „hüpfen” gegen einen Rand ist einfach nur eine halbe Sinusschwingung, oder eine ganze mit der `abs()`-Funktion, dann bekommt man zwei Hüpfer für eine volle Sinusschwingung.
The Hit-Man
User
Beiträge: 435
Registriert: Montag 20. Februar 2006, 18:11
Wohnort: Menden / Sauerland
Kontaktdaten:

Du willst also ein Logo einfach nur hin und herschwingen lassen - ohne das Logo selbst zu verformen?
richtig.

Ich sah auch gerade das du mit Deiner routine wirklich eine komplette Sinuskurve berechnet hattest.

EDIT:
t * f * 2 * pi * width
Also mit einer Zeitvariblen hatte ich damals nichts zu tun.
The Hit-Man
User
Beiträge: 435
Registriert: Montag 20. Februar 2006, 18:11
Wohnort: Menden / Sauerland
Kontaktdaten:

by the way. Ein alter Freund hatte mir ein solches Programm mal geschrieben. Ist zwar in C# aber das ganze sieht so aus:

Code: Alles auswählen

			int i;
			int numDots			= 200;			// Anzahl Werte in der Tabelle
			double minValue		= 100;			// Kleinster Wert
			double maxValue		= 200;			// Grösster Wert
			double PI			= 3.141592654f;
			double RAD			= 0.017453292f;	// PI/180

            int.TryParse(tbTableLength.Text, out numDots);
            double.TryParse(tbMinValue.Text, out minValue);
            double.TryParse(tbMaxValue.Text, out maxValue);

			double step			= 360.0f / numDots;
			int[] sinTbl		= new int[numDots];
			double mod = (maxValue - minValue);
            double mod2 = mod / 2;

			for (i = 0; i < numDots; i++)
			{
                double val = cbxCosinus.Checked ? Math.Cos(i * step * RAD) * mod2 + mod2 : Math.Sin(i * step * RAD) * mod2 + mod2;
                val = cbxCosinus.Checked ? Math.Round(Math.Cos(i * step * RAD) * mod2) : Math.Round(Math.Sin(i * step * RAD) * mod2);
				sinTbl[i] = (int)(mod2 + minValue + (int)(Math.Round(val)));
			}

muß aber gestehen, ich blicke da nicht durch. Also durch die Formel nicht.
deets

The Hit-Man hat geschrieben: Also mit einer Zeitvariblen hatte ich damals nichts zu tun.
Aber ganz bestimmt hattest du. Wenn dem nicht so waere, dann haette sich nix bewegt. Nur hast du wahrscheinlich einfach jeden Frame den lookup in deiner vorberechneten Tabelle um einen diskreten Wert verschoben. Das wahr einfacher, weil man auch ueblicherweise fixere Frameraten hatte. Aber mit zB pygame kann man das halt so nicht unbedingt machen + nimmt die clock.
BlackJack

@The Hit-Man: Die Zeitvariable und die Frequenz steckt bei den Tabellen für den C64 in der Länge der Tabelle und der Framerate in der die Werte ausgelesen werden. Man hat da ja nicht „auf die Uhr geschaut” (`t`) und danach die aktuelle Position ausgerechnet, sondern stur bei jedem Aktualisieren den nächsten Wert aus der Tabelle gelesen. Die Tabellenlänge ist meistens auf 256 oder 128 Einträge festgelegt, damit man den Zeiger in die Tabelle möglichst effizient wieder vorne anfangen lassen kann, zum Beispiel in dem man gar nix besonders tut (bei 256 Einträgen) oder Modulo 128 durch eine simple bitweise Und-Verknüfung ausdrücken kann. Die Frequenz der Schwingung hat man dann durch aktualisieren alle n Frames oder erhöhen des Tabellenzeigers um n in jedem Frame angepasst, bei einer Framerate von 50Hz.

In dem C#-Code gibt es anscheinend eine Checkbox über die man auswählen kann ob Cosinus oder Sinus verwendet werden soll. In der Schleife wird `val` definiert und berechnet *und gleich danach* wird `val` erneut berechnet, ohne dass das Ergebnis der ersten Rechnung irgendwo benutzt würde. In der Berechnung wird der Wert für `val` im letzten Schritt gerundet. Und das erste was bei der darauffolgenden Verwendung von `val` gemacht wird: Der gerade gerundete Wert wird noch einmal gerundet. m)

Die Konstante PI wird definiert, aber nicht verwendet.

Da scheint der Programmierer selber nicht so ganz gewusst zu haben was er macht. :-þ

Wenn man den unnötigen Code, den GUI-Kram, und die Cosinusvariante rauswirft, sieht es schon viel übersichtlicher aus:

Code: Alles auswählen

        int numDots     = 200;         // Anzahl Werte in der Tabelle
        double minValue = 100;         // Kleinster Wert
        double maxValue = 200;         // Grösster Wert
        double RAD      = 0.017453292f;   // PI/180

        double step  = 360.0f / numDots * RAD;
        double mod2  = (maxValue - minValue) / 2;
        int[] sinTbl = new int[numDots];
        
        for (int i = 0; i < numDots; i++)
        {
                double val = Math.Sin(i * step) * mod2;
                sinTbl[i] = (int) Math.round(mod2 + minValue + val);
        }
deets

The Hit-Man hat geschrieben:by the way. Ein alter Freund hatte mir ein solches Programm mal geschrieben. Ist zwar in C# aber das ganze sieht so aus:
Das ist eine etwas aufgeblasene Variante von meiner ersten Funktion, welche halt die Werte vorberechnet und in einer Tabelle speichert. Plus noch sin/cos gefummel, was voelliger Unsinn ist, weil sin(x) = cos(x - pi / 2), und man deshalb einfach einen Offset in die Lookup-Tabelle benutzt.
The Hit-Man
User
Beiträge: 435
Registriert: Montag 20. Februar 2006, 18:11
Wohnort: Menden / Sauerland
Kontaktdaten:

So, werde dann alles erst mal ausprobieren. Und nen großes Dankeschön für die Mühe.
The Hit-Man
User
Beiträge: 435
Registriert: Montag 20. Februar 2006, 18:11
Wohnort: Menden / Sauerland
Kontaktdaten:

so, habe ich es jetzt hin bekommen ...

Code: Alles auswählen

	def calc (self):
		self.x = self.x + self.speed
		if self.x > 360:
			self.x = self.x - 360
		self.sinus = math.sin(self.x*math.pi/180)*WIDTH/2*2
The Hit-Man
User
Beiträge: 435
Registriert: Montag 20. Februar 2006, 18:11
Wohnort: Menden / Sauerland
Kontaktdaten:

hat dann so auch hin gehauen ;) jetzt habe ich noch mal eine Frage. Ich weiß nicht ob ich dafür einen neuen Thread erstellen soll denn es geht jetzt um das weiche blittern.
deets

es heisst blitten. blittern gibt's nicht. und ich wuerde mal sagen frag halt einfach, aber ich bin auch kein forumsregelkenner.
The Hit-Man
User
Beiträge: 435
Registriert: Montag 20. Februar 2006, 18:11
Wohnort: Menden / Sauerland
Kontaktdaten:

ok, ich frag mal ;)

meine Hauptroutine sieht so aus:

Code: Alles auswählen

if __name__ == '__main__':
	myLogo = Logo (WIDTH/2, 0, 1, "data/image.png")
	
	myStars = []
	for starfield in range ( 0, HEIGHT ):
		myStars.append (Stars (starfield, 255, 255, 255))
	
	pygame.init()
	screen = pygame.display.set_mode((WIDTH, HEIGHT),
                                     pygame.DOUBLEBUF
                                        | pygame.FULLSCREEN
                                        | pygame.HWSURFACE,
									 16)                     
	clock = pygame.time.Clock()               
	while True:
		for event in pygame.event.get():
			if (event.type == pygame.QUIT
				or (event.type == pygame.KEYDOWN
					and event.key == pygame.K_ESCAPE)):
                
					pygame.quit()
		screen.fill (BLACK)
		
		
		for starfield in range (0, HEIGHT):
			myStars [starfield].draw (screen)
			myStars [starfield].calc ()
		
		myLogo.calc ()
		myLogo.draw (screen)
		pygame.display.update()
		clock.tick(FPS)
Ich habe zwar den DoubleBuffer eingestellt, doch blitte ich immer gleich auf das HauptSurface. Und das scheint alles bischen zu ruckeln. Damals hatte ich mal unter Allegro nen Demo programmiert und nen TribleBuffer eingestellt, also zwischen 3 Surfaces geblittert. Allerdings war auch das echt ruckelig. Im Netz fand ich die Sache mit der Clock ( den ich auch hier verwende ), nur viel bringen tut das irgendwie nichts.
Würde nur mal gerne wissen, wie man das besser lösen kann?
BlackJack

@The Hit-Man: Ob `screen` wirklich direkt der angezeigte „Framebuffer” ist, kann selbst ohne DOUBLEBUF nicht garantiert werden, mit DOUBLEBUF ist es er das dann aber ganz sicher nicht. Dafür sorgt SDL schon.

Beim ruckeln ist die Frage wo das herkommt. Sind die FPS zu gross gewählt und der Code kommt manchmal nicht hinterher, oder ist der Puffer/Anzeigewechsel nicht synchron zum Bildaufbau? Oder beides? Und der gleiche Code kann auf einem anderen Rechner mit anderer Hardware, anderen Treibern, und einem anderen Monitor ganz anders aussehen. Und natürlich auch andersherum: Wenn es bei Dir perfekt aussieht, ruckelt es vielleicht bei jemand anderem. Die Zeiten das jeder den gleichen Videochip und einen Röhrenmonitor mit 50Hz hat und die Hardware es erlaubt sogar genauer als eine Rasterzeile synchronisiert etwas in die Videochip-Register zu schreiben, sind schon etwas länger vorbei.

Das war damals für mich schon eine herbe Enttäuschung als ich auf dem PC feststellen durfte, dass man bei VGA-Karten nur abfragen kann wann der Rasterstrahl nach dem Bildaufbau von unten wieder nach oben abgelenkt wird. Und das man sich darüber nicht mal per Interrupt informieren lassen kann, sondern tatsächlich in einer Schleife auf ein Bit in einem Register warten muss. Das war man vom C64 mehr Komfort gewohnt. :-)

Die Namensgebung ist teilweise übrigens irreführend. `starfield` heisst Sternenfeld und Du bindest da kein Sternenfeld dran, sondern einen Index. Der in der zweiten Schleife über die Sterne zudem noch unnötig ist, man kann *direkt* über die Elemente von `myStars` iterieren. Und eine Klasse die `Stars` heisst, aber nur *einen* Stern repräsentiert, ist auch komisch.
The Hit-Man
User
Beiträge: 435
Registriert: Montag 20. Februar 2006, 18:11
Wohnort: Menden / Sauerland
Kontaktdaten:

Die Namensgebung ist teilweise übrigens irreführend. `starfield` heisst Sternenfeld und Du bindest da kein Sternenfeld dran, sondern einen Index. Der in der zweiten Schleife über die Sterne zudem noch unnötig ist, man kann *direkt* über die Elemente von `myStars` iterieren. Und eine Klasse die `Stars` heisst, aber nur *einen* Stern repräsentiert, ist auch komisch.
Naja, ich arbeite ja noch dran ;) Ich verstehe schon was du meint, mit dem Rasterstrahl. Aber es muß doch eine Lösung geben, alles weich hin zu bekommen. Wenn ich mir WC3 unter meinem Wine anschaue, läuft das ja auch flüssig, auch wenn das Game schon echt alt ist. Wenn ein Rechner zu langsam ist um irgendetwas flüssig wieder zugeben, ist das natürlich verständlich. Allerdings auf dem Rechner, auf dem ich das Programmiere, ist mein GFX-Treiber ( Debian/Wheezy ) 1400MHz, 1,2GB RAM, 3D Beschleunig. Ein Freund sagte mir, er benutze OpenGL und dort hätte man einen V-Sync. Obwohl er sich im Moment mit dem gleichen Problem unter OpenGL rumschlägt. Es muß doch was transparentes geben, irgendwie. Irgenwelche Shooter und sonstige Sachen machen das doch auch?

Hatte mal selbstgemachte Demos auf der PS1 gesehen, die auch auf SDL aufsetzen ( gibts ja fast auf jedem System ) und dort ruckelte auch nichts.
BlackJack

WC3 ist jetzt Wing Commander oder Warcraft? Ist ja beides älter. :-) Rechnergeschwindigkeit, Grafikkarte, und RAM können noch so toll sein, wenn der Flaschenhals die Übertragung der Daten vom Rechner in die Grafikkarte ist, kann das trotzdem zu langsam sein. Hardwarebedingt. Irgendwelche Shooter und sonstige Sachen haben teilweise genau das gleiche Problem. Wenn Du mehr Kontrolle haben möchtest, zum Beispiel bei den Sachen die im Arbeitsspeicher passieren vs. was im Speicher der Grafikkarte passiert, musst Du auf den relativen Komfort von SDL verzichten und OpenGL verwenden.

Ein allgemeiner PC und SDL ist nicht mit einer PS1 und SDL vergleichbar, weil SDL auf dem PC mit allen möglichen Hardwarekombinationen klar kommen muss, und bei der PS1 ziemlich fix ist, auf welches Backend SDL zugeschnitten und optimiert werden kann. Was immer da drunter liegt *muss* VSYNC bieten, denn alles andere wäre bei einer Spielkonsole unsinnig.

Was für ein Ruckeln hast Du denn eigentlich? Tearing, also dass das Bild im Aufbau deutlich in zwei oder mehr horizontale Streifen geteilt ist, die nicht ganz zusammen passen? Oder stockt die ansonsten flüssige Animation manchmal einen Frame lang? Bei letzterem kann es an *vorhandenem* VSYNC liegen, nämlich dann wenn Deine gewünschte FPS nicht mit dem übereinstimmt was der Monitor an FPS vorgibt. Also das übliche Problem was man mit C64-Emulatoren hat, weil die wenn sie auf 100% laufen 50 FPS erzeugen, dass aber kaum ein Monitor als Refresh-Rate anbietet. Bei Röhrenmonitoren möchte man das ja auch gar nicht — da könnte man sich noch mit 100 Hz behelfen, wenn es der Monitor kann, aber bei LCDs besteht in der Regel ja nicht einmal die Möglichkeit etwas zu wählen. Da muss man nehmen was der Monitor bietet.
Antworten